Skip to content

Containers

What you’ll build: Two isolated containers running in the same JS process, each with a private filesystem and process tree.

Prerequisites: Filesystem →


A fishbowl container is a process group with:

  • A private filesystem namespace
  • A layered image built from overlay filesystems
  • Its own init system and service graph
  • A private /dev and /proc

The host kernel manages all processes. Containers cannot see each other’s process trees.

instance.run(cmd) executes a shell command and returns its output. instance.shell() starts an interactive session.

import { Unix } from '@fishnet/core'
import { stdSystem } from '@fishnet/core/presets'
import { nodeRuntime } from '@fishnet/runtime/platform/node'
const image = Unix().use(stdSystem()).build()
// Boot two independent instances from the same image
const a = await image.boot(nodeRuntime())
const b = await image.boot(nodeRuntime())
// Each instance has its own process table and filesystem
await Promise.all([a.run('echo hello from A'), b.run('echo hello from B')])

Each boot gets its own overlay layer — writes from one instance never affect another. You can also bake files into the base image before freezing it.

import { Unix, memoryFS, overlayFS } from '@fishnet/core'
import { stdSystem } from '@fishnet/core/presets'
import { nodeRuntime } from '@fishnet/runtime/platform/node'
const base = memoryFS()
base.writeFile('/etc/motd', 'Welcome to container A')
base.freeze()
const upper = memoryFS()
const image = Unix()
.use(stdSystem())
.use({ mounts: [{ path: '/', server: overlayFS(upper, base) }] })
.build()
const instance = await image.boot(nodeRuntime())
// cat /etc/motd → "Welcome to container A"
// Writing to /etc/motd writes to `upper` only