Isolated Linux machines for agents. On your laptop, your servers, or our cloud.
This repo holds the dock CLI, with its built-in local engine, and the docklet, the host agent for Dock. One docklet runs on each machine that hosts docks. It boots, pauses, snapshots, resumes and destroys docks, and reports what the host actually has.
curl -fsSL https://usedock.io/install | sh # Linux, macOS
irm https://usedock.io/install.ps1 | iex # Windows (PowerShell)Both installers verify the release checksums and cosign signatures before installing the dock CLI.
With no account, dock runs docks on your own machine (the built-in local context). You need QEMU and KVM on Linux (x86_64 or arm64), or QEMU and Windows Hypervisor Platform on Windows x86_64; dock doctor tells you what is missing.
curl -fsSL https://usedock.io/install | sh
dock new demo
dock exec demo -- uname -aThe first dock new downloads the default ubuntu-24.04 Dockyard image once. On Windows, install with the PowerShell line above.
The first dock new downloads the image; the recording skips that wait.
Docs: quickstart, CLI, local docks, Windows, Dockyard images.
Questions and ideas: GitHub Discussions.
Status: the
dockCLI ships in signed releases (since v0.1.0). From v0.2.0 it runs docks locally with no account on Linux (x86_64, and arm64 from v0.3.0) with KVM and Windows x86_64 with Windows Hypervisor Platform, via QEMU; macOS builds but is untested. The docklet host agent is early, and Windows and macOS fleet hosts are planned.
- Runs docks as Firecracker microVMs (under
jailer) on Linux with KVM, or as gVisor (runsc) containers on Linux with Docker. - Enforces each dock's CPU and memory with cgroups, gives each dock a fixed-size disk (a sparse rootfs on microvm, a size-limited work volume at
/home/user/workon gVisor), and gives it a TAP or veth network on a host-local subnet. - Moves snapshot data to the object store over presigned URLs the control plane mints, or through its object proxy; the host never holds store credentials.
- Connects outbound only to the control plane fleet API: it registers, sends heartbeats with its inventory and stats, long-polls for commands and posts results. No inbound port, no per-host address.
- Serves its own HTTP API on loopback only.
- Fails closed: a host is not offered work until its readiness checks pass.
| Host | Engine | Status |
|---|---|---|
| Linux with KVM | Firecracker | Early, in signed releases |
| Linux with Docker | gVisor | Early, in signed releases |
| Windows x86_64 (fleet host) | docklet daemon on Windows | Planned |
| macOS, Apple silicon (fleet host) | docklet daemon on macOS | Planned |
A snapshot moves only between hosts with the same engine and CPU architecture.
The dock binary carries its own engine and runs docks on your own machine with QEMU. The engine needs QEMU and the machine's hardware accelerator; it never falls back to software emulation on its own.
| Machine | Accelerator | Status |
|---|---|---|
| Linux x86_64 | KVM (/dev/kvm) |
Available |
| Linux arm64 | KVM (/dev/kvm) |
Available from v0.3.0 (tested under emulation so far) |
| Windows x86_64 | Windows Hypervisor Platform (WHPX) | Available |
| macOS | Hypervisor.framework (HVF) | Untested, planned |
dock doctor checks what is missing. On Windows, enable the platform once (elevated PowerShell, then reboot) and install QEMU:
Enable-WindowsOptionalFeature -Online -FeatureName HypervisorPlatform
winget install SoftwareFreedomConservancy.QEMUOn a Linux machine without usable KVM (for example a cloud VM without nested virtualization), opt in to gVisor containers instead: dock new --runtime gvisor or DOCK_LOCAL_RUNTIME=gvisor. There is no silent fallback: without the flag, local docks stay QEMU only.
- Needs Docker Engine and the gVisor
runscruntime registered with it (install gVisor, thensudo runsc installandsudo systemctl restart docker).dock newchecks both before building anything. Forsudoinside docks, also register a second runtime once (below). The first dock builds a smalldock-local-gvisor:<hash>image withdocker build(Ubuntu 24.04, python3, git, curl, sudo); nothing is published. - Optional, for
sudoand a persistent rootfs: gVisor ignores setuid bits unless runsc runs with--allow-suid, and by default it keeps rootfs writes in an overlay that is discarded when the container stops (--overlay2=nonesends them to the container layer instead). Register a separate runtime once:sudo runsc install --runtime=runsc-suid -- --allow-suid --overlay2=none && sudo systemctl restart docker. It is a second entry for the same runsc binary, so your other runsc containers keep the default and are not affected; only docks created after it exists use it (dock newprints a note when it is missing, anddock doctorshows which case you are in). A dock keeps the runtime it was created with for life, so existing docks do not gain sudo. Without it gVisor docks work but have nosudo(no root, noapt-get install) and only/home/user/workpersists across stop and resume.runsc-suidis only used when it points at a runsc binary and was registered with both flags; otherwise it is treated as missing. - On Linux, the
no_acceleratorerror anddock doctorpoint to this option when KVM is unusable.dock doctorshows an informationalgvisorrow that never makes it fail. - With the
runsc-suidruntime, like a QEMU dock, the whole filesystem persists (with plainrunsconly/home/user/workdoes):dock stopstops the container and keeps it,dock resumestarts it again, so packages you installed withsudo apt-get installsurvive./home/user/workis also a named Docker volume, so that data outlives a lost container.dock rmremoves the container, volume and network. A stopped dock holds no Docker network, and keeps its local port: if another program takes that port,dock resumefails withport_in_useand works again once the port is free. exec,sshandportwork as for QEMU docks, through the guest agent on127.0.0.1.dock ls --jsonshowsruntime: gvisor. Commands run asuser; with therunsc-suidruntimesudois passwordless and is root inside the sandbox.no-new-privilegesis not set on these docks (it would block sudo).- Isolation: gVisor is a strong sandbox. A user-space kernel written in Go intercepts the dock's system calls and only a small, filtered set reaches the host kernel; Google runs untrusted code on it (GKE Sandbox). It is a different boundary from a hardware VM, not a weak one. Root in the dock is root inside the sandbox, not on the host. The container runs with Docker's default capabilities, never
--privileged, and with CPU, memory and process limits. Being able to use Docker is root-equivalent on most setups, so anyone who can run Docker on the machine can reach every gVisor dock. - Real differences from QEMU docks:
- Each dock gets its own Docker bridge network (
dock-local-<id>-net), so it cannot reach your other containers or other docks, but it can reach the host's network services through the bridge gateway (for example a service listening on the host's0.0.0.0) and the internet, unlike a QEMU dock, whose guest refuses the host loopback gateway. - DNS uses the host's upstream resolvers.
- No Docker or systemd inside by default, and one built-in image instead of Dockyard images.
--diskis not enforced: the work volume and, withrunsc-suid, everything written elsewhere in the dock live in Docker's storage (/var/lib/docker) with no size limit.docker container pruneordocker system prunedeletes a stopped dock's container and everything installed in it; only/home/user/work(a named volume) survives.dock branchworks: the source is stopped if running, its container filesystem is committed to adock-local-gvisor-branch:<id>image and its work volume copied (by a short-lived, network-less runsc container), then the source is resumed and the branch starts as a new dock with its own token, port and network. With therunsc-suidruntime the branch carries rootfs changes (installed packages) too; with plainrunsconly/home/user/workcarries over.dock rmremoves the branch image. A committed image keeps a copy of the source's rootfs changes on disk until the source and all branches made from that commit are removed.--network offworks: the container runs with--network none, so it has no internet, no DNS and no route to the host or to other docks (the sandbox keeps only its own loopback). With no published port,dockreaches the guest agent throughdocker exec -i <container> python3(a small relay to the agent on the sandbox loopback), sodock exec,dock sshanddock portwork as on any other dock. Each agent call costs onedocker exec, so they are slower than on a network-on dock. Stop, resume and branch keep the dock network-off, and a network-off dock never creates or connects a Docker network.- Not supported:
dock pushanddock pull. - Some syscall-heavy or file-I/O-heavy workloads are slower than in a VM.
- Each dock gets its own Docker bridge network (
- If the
dockerbinary is gone,dock rmstill removes the record and warns with the names to clean up by hand; if Docker is installed but its daemon is down, it fails withdocker_unavailableand you can retry after starting Docker.
cmd/docklet/- the daemon.cmd/gen-fast-agent/- guest agent generator.cmd/dock/+internal/cli/- the portabledockCLI.api/v1/- the versioned contract with the control plane.internal/-agent(fleet channel),fc(Firecracker),container(gVisor),host(orchestration),snapmgr(snapshots),net,egress,disk,store,serverand more.scripts/- host bootstrap, image and kernel builders.DEPLOY.md- the full host deploy guide.
One command per machine, run as root on Ubuntu from a checkout of this repo. It installs the runtime, the signature-verified docklet release and a systemd unit, then enrolls the host with your control plane (usedock/dock):
sudo bash scripts/bootstrap-host.sh \
--fleet-url https://dock.example.com \
--fleet-token "$FLEET_TOKEN" \
--runtime microvm # or gvisorPick microvm on Linux with KVM or gvisor on any Linux with Docker. The script is re-runnable. See DEPLOY.md for flags, base images and troubleshooting.
A microvm host boots from the signed Dockyard ubuntu-24.04 (pulled at bootstrap; --build-base or an unreachable Dockyard falls back to building it on the host), so a dock's whole disk can move between hosts and contexts. A snapshot with full: true writes a checksummed rootfs.ext4.zst, and restoring one verifies it. See DEPLOY.md.
cmd/dock runs docks locally (the built-in local context, no account) or talks to the Dock control plane /v1 API (the cloud context and added URLs; not this daemon). It builds on Linux, macOS and Windows; released binaries come from the installer above.
go build -o dock ./cmd/dock # or use the installed release
dock doctor # what local docks need: QEMU, accelerator, disk, Dockyard
dock images # Dockyard images; `dock images pull ubuntu-24.04` downloads one
dock new web # with no account this boots a local dock (downloads the image once)
dock login # prompts without echo; or: echo "$DOCK_API_KEY" | dock login
dock new web --cpus 4 --memory 8gb --disk 40gb --env staging # returns once the dock is ready
dock push web --to lab # copy a stopped local dock's disk to a control plane context (--name, --stop, --rm)
dock pull web --from lab --name web-local # copy a fleet dock's disk to a new stopped local dock (Dockyard-image docks only)
dock ls
dock exec web -- uname -a # exits with the command's exit code
dock ssh web # runs ssh against the Dock SSH gateway (--print to show it)
dock port web 3000 # prints the public URL
dock branch web --count 2
dock stop web && dock resume web && dock rm web-
Every command accepts
--jsonand prints the API data. Errors printerror: <code>: <message>to stderr and exit 1; usage errors exit 2. -
Sizes:
8gb,8g,8192mb; a plain number is MiB for memory and GiB for disk. -
<dock>is a name or id.dock newreads./dock.toml(name,image,cpus,memory,disk,environment); flags override it and unknown keys are an error. -
Local context: the default until
dock loginstores a key orDOCK_API_KEY/DOCK_API_URLis set; switch withdock context use local|cloud. Local docks live under$XDG_DATA_HOME/dock(Linux),~/Library/Application Support/dock(macOS) or%LOCALAPPDATA%\dock(Windows) (DOCK_DATA_DIRoverrides): a qcow2 overlay on the cached Dockyard base image, the agent token, forwarded port and pid.--jsonkeeps the API's keys;runtimeisqemu(orgvisor, see above) and a stopped dock isarchived. Local differences:dock new --network offblocks the dock's network (QEMUrestrict=on; IPv6 is always disabled in the guest network),dock portforwards a dock port to127.0.0.1in the foreground (no public URL),dock sshopens a shell through the guest agent,dock branchcopies the stopped disk (a running dock is stopped, copied and resumed; there is no live fork), anddock stoppowers the guest off and keeps the disk.execreturns when the command ends. Settings:DOCK_QEMU(qemu-system binary),DOCK_DOCKYARD_URLandDOCK_DOCKYARD_SHA256(use another image index; it is unpinned and warned about, and verified only when the hash is given). The dock binary pins the images release tag and the sha256 of itsdockyard.json, and refuses an index that does not match.DOCK_LOCAL_ACCEL=tcgallows slow software emulation (tests only; local docks never fall back to it on their own). -
Local network isolation, trust properties: QEMU user-mode networking (slirp) maps the gateway
10.0.2.2to the host's127.0.0.1, and QEMU has no option to turn that off (itsrestrict=onis all or nothing). With--network on(the default) the dock therefore asks its guest to refuse connections to10.0.2.2(an iptables rule applied at boot from the dock's config; DNS, the internet and the agent forward are unaffected). This stops code that accidentally probeslocalhostservices,dock portforwards of other docks included, but it is a guest-side control: root inside the dock can remove the rule, and images released before this change ignore it. Each dock's agent forward needs that dock's random token, so one dock cannot run commands in another. Treat a network-on local dock as able to reach your localhost services if it is hostile; use--network offfor untrusted code. A user-space network stack indock(a host-enforced fix) is tracked in #98. -
Contexts:
dock context add|use|ls|rm. The built-incloudcontext points athttps://api.usedock.io. Config lives inos.UserConfigDir()/dock/config.toml(mode 0600).DOCK_API_URL,DOCK_API_KEYandDOCK_WORKSPACEoverride the active context. -
Workspace for
dock new:--workspace, thenDOCK_WORKSPACE, then the context default (dock context use <name> --workspace <id>), then your only workspace. With several workspaces and none chosen,dock newfails and lists them. -
Push and pull move the disk only (no memory, no running processes), between
localand a control plane context with a microVM host.dock pushflattens the stopped dock's disk, compresses it with zstd, checks it with sha256 and uploads it in retried parts;dock pullexports, downloads with resume, verifies, and registers a stopped local dock on the Dockyard image it came from. Runtime files are never sent. The flattened disk does hold the guest's own copy of the dock's local token, sodock pushrotates the local token (the copy that leaves stops working), and a pulled dock gets a new local token. A push or pull checks free space first, and staletmp/xfer-*leftovers from a crashed run are removed on the next one. -
Not yet:
dock cp(the API has no file endpoint for live docks) and streamingexecoutput (the exec API returns the bounded result when the command ends).
Linux only, Go 1.25.
go build ./...
go vet ./...
go test ./...
go test ./internal/host/ # one packageOn Windows or macOS, GOOS=linux go vet ./... and go test -exec=true ./... check that everything compiles; CI runs the real tests. Every behavior change ships with go test coverage. Fakes live in *_test.go, never in production code. api/v1 is the versioned contract with the control plane; publishing JSON fixtures for every call is planned. Agents working in this repo: see AGENTS.md.
Contributions are welcome. Every contributor signs the CLA; the CLA bot asks on your first pull request in each repository. See CONTRIBUTING.md.
Report vulnerabilities to security@usedock.io, not in public issues. See SECURITY.md. Releases are signed with cosign (keyless, GitHub OIDC) and ship checksums.txt; the install script stops if the checksum or signature is missing or bad. See the release verification section of DEPLOY.md.
Copyright (C) 2026 Paranjay.
