vz
Cross-platform runtime for containerized workloads and macOS VM automation.
vz provides one CLI for:
- OCI image and container lifecycle
- Multi-service stacks from Compose files
- macOS VM provisioning and control (Apple Virtualization.framework)
Typical use cases:
- Run isolated build/test workloads from OCI images
- Launch local multi-service environments from Compose
- Automate deterministic macOS VM test sandboxes
Why vz
- One interface, multiple runtimes. Use the same CLI flow on macOS and Linux.
- Container-native. Pull, run, create, exec, log, stop, and remove OCI workloads.
- Stack-aware. Bring up complete Compose apps with events, logs, and service exec.
- VM automation on macOS. Provision, run, exec, save, and restore macOS VMs over vsock.
- Script-friendly. Consistent command model with
--json support across commands.
Install
curl -sSf https://raw.githubusercontent.com/gpu-cli/vz/main/scripts/install.sh | sh
This installs pre-built binaries (signed + notarized) and the Linux kernel to ~/.vz/bin/.
Requires macOS on Apple Silicon.
Options:
VZ_VERSION=0.3.0 — pin a specific version
VZ_NO_LINUX=1 — skip Linux kernel download
Install from source
# Requires Rust 1.85+
cargo install --git https://github.com/gpu-cli/vz.git vz-cli
vz self-sign # apply Virtualization.framework entitlements
Build the Linux kernel (for source installs)
cd linux && make docker-build # requires Docker
mkdir -p ~/.vz/linux && cp linux/out/{vmlinux,initramfs.img,youki,version.json} ~/.vz/linux/
The default kernel profile is developer and keeps nested virtualization for
Virgil-style Firecracker host VMs. To build the constrained container sandbox
bundle, use:
cd linux && make docker-build KERNEL_PROFILE=container
Release CI caches the developer/container kernel images by kernel inputs, then
rebuilds the initramfs and metadata for each vz release.
Platform support
- Linux: container + stack commands
- macOS (Apple Silicon): container + stack commands, plus
vz vm ...
Quick start
1. Run commands in a Linux VM
cd your-project
# Generate a vz.json config (auto-detects Rust, Node, Python, Go)
vz init
# Run any command inside the Linux VM
vz run echo "hello from Linux"
# Compile and run a Rust project
vz run cargo build
vz run cargo test
# Open an interactive shell
vz run -i bash
# Check VM status
vz status
# Stop the VM when done
vz stop
The first vz run boots a Linux VM (~3s), pulls the base image, and runs setup commands from vz.json. Subsequent runs reuse the VM and skip setup (cached by hash).
vz.json
{
"image": "ubuntu:24.04",
"workspace": "/workspace",
"mounts": [{ "source": ".", "target": "/workspace" }],
"setup": [
"apt-get update",
"apt-get install -y build-essential curl"
],
"env": { "PATH": "/root/.cargo/bin:/usr/local/bin:/usr/bin:/bin" },
"resources": { "cpus": 4, "memory": "8G" }
}
2. Run a Compose stack
# Start services
vz stack up -f compose.yaml -n demo
# Inspect and stream logs
vz stack ps demo
vz stack logs demo --service web --follow
# Tear down
vz stack down demo --volumes
Stack networking defaults to service identity inside the stack network.
Host-facing port publishing is explicit opt-in via Compose host bindings
(HOST:CONTAINER); container-only ports remain internal.
Reaching macOS host services from inside a container
Every stack-managed container resolves the hostname host.vz.internal
to the macOS host's NAT gateway IP (192.168.64.1). This is the Docker
Desktop equivalent of host.docker.internal and is injected automatically
into each container's /etc/hosts.
# Inside a sandboxed container:
curl http://host.vz.internal:18080/ # reaches the host service
Caveat — bind to 0.0.0.0, not 127.0.0.1: services on the macOS host
that listen only on 127.0.0.1 are not reachable via the NAT gateway
IP. macOS's TCP stack does not route inbound NAT traffic to its own
loopback. Bind your host service to 0.0.0.0 (or to 192.168.64.1
explicitly) so the guest can reach it. The address itself is hardcoded
because Virtualization.framework provides no API to query Apple's NAT
gateway — see vz_runtime_contract::HOST_INTERNAL_GATEWAY_IPV4.