A lightweight, fast container runtime for macOS built from scratch in Rust.
Vessel is a Docker Desktop alternative that boots a minimal Linux VM via Apple's Virtualization.framework and runs containers through containerd + crun. No Electron, no Kubernetes, no bloat.
- Fast: VM boots in ~7 seconds on Apple Silicon
- Lightweight: ~160MB total (kernel + initramfs), no background Electron app
- Native: Uses Virtualization.framework directly — near-native hypervisor performance
- Compatible: Docker Engine API on a Unix socket, works with
DOCKER_HOST - Multi-arch: ARM64 native + x86_64 via Rosetta 2 translation
# Prerequisites: Rust, Docker (for building initramfs), Xcode CLI tools
# Install musl cross-compiler
brew install filosottile/musl-cross/musl-cross
# Build everything
make build # Build + codesign the daemon
cd vm && make all && make install # Build VM image
# Run
make run # Start vessel daemon
# In another terminal
vessel pull alpine:latest
vessel run -d -p 8080:80 alpine sh -c "while true; do echo -e 'HTTP/1.0 200 OK\r\n\r\nHello!' | nc -l -p 80; done"
curl http://localhost:8080 # Hello!vessel pull nginx:alpine # Multi-arch OCI pull
vessel run -d alpine sleep 120 # Run detached
vessel run -d --name myapp alpine sleep 120 # Named containers
vessel run -d -p 8080:80 alpine ... # Port forwarding
vessel run -d -e FOO=bar alpine env # Environment variables
vessel run -d -v ~/project:/app alpine ls /app # Volume mounts
vessel exec <id> echo hello # Execute in container
vessel exec -it <id> sh # Interactive shell
vessel logs <id> # View logs
vessel logs -f <id> # Follow logs
vessel ps # List containers
vessel stop <id> # Graceful stop (SIGTERM then SIGKILL)
vessel rm <id> # Remove container
vessel images # List pulled imagesvessel build -t myimage:latest . # Build via BuildKitvessel version # API version
vessel info # CPUs, memory, OS
vessel start # Check daemon statusmacOS Host
vessel (CLI)
|
| Unix socket (~/.vessel/vessel.sock)
v
vesseld (daemon)
|-- Docker Engine API server (axum, HTTP/1.1)
|-- OCI image registry client
|-- Port forward manager
|-- VM lifecycle manager
|
| Virtualization.framework
v
Linux VM (custom kernel 6.6.10, arm64)
|
| tmpfs root (chroot from initramfs)
|
|-- containerd ──> crun (OCI runtime)
|-- BuildKit daemon
|-- vessel-agent
| |-- vsock:1024 (RPC channel)
| |-- vsock:1025 (port forwarding data)
| |-- vsock:1026 (interactive exec)
|
|-- virtiofs (/host -> macOS home directory)
|-- Rosetta 2 (x86_64 binary translation)
| Channel | Protocol | Purpose |
|---|---|---|
| CLI <-> Daemon | HTTP over Unix socket | Docker Engine API |
| Daemon <-> Agent | vsock:1024 | JSON-framed RPC (container ops) |
| Daemon <-> Agent | vsock:1025 | Raw TCP forwarding (port mapping) |
| Daemon <-> Agent | vsock:1026 | Interactive exec streaming |
| Host <-> VM | virtiofs | File sharing (volumes, image layers) |
vessel/
crates/
vessel-cli/ CLI binary (clap)
vessel-daemon/ Daemon binary (axum, Virtualization.framework FFI)
vessel-agent/ VM-side agent (containerd gRPC, vsock)
vessel-api/ Shared Docker API types (serde)
vessel-vsock/ vsock framing protocol
proto/ containerd gRPC proto definitions
vm/
kernel/ Kernel download scripts
initramfs/ Init scripts + build system
-
vessel pull: Daemon fetches OCI manifest from Docker Hub (resolves arm64/amd64), downloads layers to~/Library/Application Support/Vessel/images/, transfers to VM via virtiofs, imports into containerd viactr -
vessel run: Daemon sends create+start to agent over vsock, agent runsctr run --net-host --privilegedwith the image, sets up port forwarding listeners if-pspecified, mounts virtiofs paths for-vvolumes -
Port forwarding (
-p 8080:80): Daemon listens on host port 8080, opens vsock connection to agent port 1025, agent connects to container's port 80 on localhost (shared network namespace), bidirectional relay -
Volume mounts (
-v ~/project:/app): Host path translated to VM virtiofs path (/Users/x/project->/host/project), mounted inside container viamount -t virtiofs+mount --bind -
Interactive exec (
-it): CLI connects to daemon's TCP exec proxy (port 9800), daemon opens vsock to agent port 1026, agent runsctr task exec --tty, terminal set to raw mode for bidirectional streaming
- macOS 13+ (Ventura or later)
- Apple Silicon or Intel Mac
- Rust toolchain (
rustup) - Docker or OrbStack (for building the initramfs)
- Xcode Command Line Tools (
xcode-select --install)
# 1. Install musl cross-compiler for Linux agent binary
brew install filosottile/musl-cross/musl-cross
rustup target add aarch64-unknown-linux-musl
# 2. Build Rust binaries
make build
# 3. Build VM image (kernel + initramfs)
cd vm
make all # Downloads kernel, cross-compiles agent, builds initramfs
make install # Copies to ~/Library/Application Support/Vessel/
# 4. Run
make run # Builds, codesigns, and starts the daemon| Target | Description |
|---|---|
make build |
Build + codesign daemon (debug) |
make build-release |
Build + codesign (release) |
make run |
Build, sign, and run daemon |
make test |
Run all workspace tests |
make vm-build |
Build kernel + initramfs |
make vm-install |
Install VM image |
make vm-rebuild |
Rebuild initramfs with latest agent |
make clean |
Clean build artifacts |
Daemon socket: ~/.vessel/vessel.sock
Override with environment variable:
export DOCKER_HOST=unix://$HOME/.vessel/vessel.sock
# Now `docker` CLI works against Vessel
docker ps
docker imagesVM image location: ~/Library/Application Support/Vessel/
vmlinux— Linux kernel (ARM64 Image)initramfs.gz— Root filesystem with containerd + crun + agent
- Kernel: Custom Linux 6.6.10 with virtio, vsock, overlayfs, virtiofs, FUSE built-in
- Container runtime: crun (C-based OCI runtime, lighter than runc)
- Image builder: BuildKit (Dockerfile support)
- Networking: Containers share VM network namespace (
--net-host), port forwarding via vsock data channels - Storage: tmpfs root (containers are ephemeral per VM boot), overlayfs snapshotter for container layers
- Rosetta: x86_64 ELF binaries translated via Apple's Rosetta 2 inside the VM
- 53 commits from design to working runtime
- ~5000 lines of Rust code across 5 crates
- 46 unit tests + 3 integration smoke tests
- ~160MB VM image (40MB kernel + 120MB initramfs)
- ~7 second VM boot time on Apple Silicon
MIT