Bootstrap a bare VM
Install Docker or rootless Podman on a fresh Debian or Ubuntu machine with coma machine bootstrap, after reviewing the plan.
A fresh VM usually has no container engine. coma machine bootstrap prepares it: it creates COMA's directories on the machine and, with --engine, installs Docker or Podman and sets it up for your SSH user.
Add the machine first (Add a machine). When COMA finds no engine, coma machine add ends with the bootstrap command as its Next: line.
Run it
coma machine bootstrap <name> --engine docker|podmancoma machine bootstrap dev --engine dockerFor rootless Podman instead:
coma machine bootstrap dev --engine podmanOn a machine without an engine, --engine is required; without it, bootstrap fails with engine_selection_required.
The plan
Bootstrap first inspects the machine without changing it. It shows the plan, with every command it would run, and asks before it applies anything:
Bootstrap plan for dev (as you): 4 of 4 step(s) to apply
pending create-coma-dir
Create COMA's directories under ~/.coma (owner-only)
$ mkdir -p "$HOME/.coma" && chmod 700 "$HOME/.coma" && mkdir -p "$HOME/.coma/bin" "$HOME/.coma/run" "$HOME/.coma/cache" "$HOME/.coma/state"
pending install-docker [sudo]
Install Docker from the distribution's packages (docker.io)
$ sudo -n env DEBIAN_FRONTEND=noninteractive apt-get -q -o DPkg::Lock::Timeout=300 update
$ pkgs=""; for p in docker-buildx docker-compose-v2; do if apt-cache show "$p" >/dev/null 2>&1; then pkgs="$pkgs $p"; fi; done
$ sudo -n env DEBIAN_FRONTEND=noninteractive apt-get -q -o DPkg::Lock::Timeout=300 install -y docker.io $pkgs
pending start-docker [sudo]
Start the Docker service now and at boot
$ sudo -n systemctl enable --now docker
pending docker-group [sudo]
Add you to the docker group so COMA reaches the engine without sudo (this is root-equivalent access on the machine)
$ sudo -n usermod -aG docker "$(id -un)"Each step is satisfied (nothing to do) or pending (apply would run its commands). Commands are listed only for pending steps.
To see the plan and change nothing, add --plan:
coma machine bootstrap dev --engine docker --planConfirmation
In a terminal, COMA asks Apply 4 step(s) to dev? before it changes anything. For scripts and coding agents, pass --yes. Without a terminal and without --yes, bootstrap fails with confirmation_required and changes nothing.
Steps marked sudo
Steps marked [sudo] need passwordless sudo on the machine (they run sudo -n, which never prompts). If your SSH user does not have it, the plan says which step is blocked, and applying fails with bootstrap_privilege_required before anything changes. Run the plan's sudo commands yourself, or grant passwordless sudo, then run bootstrap again. As root, no step needs sudo.
What it installs
Packages come from the distribution's own signed repositories, through apt. There is no third-party repository and no curl | sh.
Docker (--engine docker):
| Step | What it does |
|---|---|
create-coma-dir | Creates ~/.coma (mode 0700) and its bin, run, cache and state directories |
install-docker | Installs docker.io, plus docker-buildx and docker-compose-v2 where the distribution has them |
start-docker | Starts Docker now and at boot |
docker-group | Adds your user to the docker group; this is root-equivalent access on the machine |
Podman (--engine podman):
| Step | What it does |
|---|---|
create-coma-dir | Creates ~/.coma and its directories |
install-podman | Installs podman, uidmap and dbus-user-session |
enable-linger | Keeps your user services running without a login session, so rootless containers and the API socket survive disconnects |
podman-socket | Starts the rootless Podman API socket now and at boot |
As root, Podman runs rootful: bootstrap starts the system Podman socket and skips the linger step.
apt waits up to five minutes for a package lock held by unattended upgrades, which is common on a freshly booted cloud VM.
Loopback publishing for containers started outside COMA
Containers started through COMA publish their ports on 127.0.0.1 on the machine. Containers started some other way, for example over a separate SSH session, follow Docker's default, which publishes on every interface. --harden-publish adds one more Docker step, engine-default-loopback-publish:
coma machine bootstrap dev --engine docker --harden-publishIt merges "ip": "127.0.0.1" into /etc/docker/daemon.json, keeps the old file as daemon.json.coma-bak, and restarts Docker. It refuses to rewrite a daemon.json that is not a JSON object. The plan marks the step restarts the engine. See Ports.
Verify and resume
Every step is a read-only check plus the commands that satisfy it.
- Verify after apply. After running a step, COMA runs its check again and moves on only if it passes. When all steps are done, COMA refreshes the machine's inventory over a new SSH connection and fails unless the engine you asked for is healthy.
- Resume. A failed step stops the run with
bootstrap_failedand the step's ID. Fix the cause and run the same command again: satisfied steps are skipped, and bootstrap continues from the step that failed. - Idempotent. On a machine that is already ready, bootstrap changes nothing:
dev is already bootstrapped; nothing to changeAfter a successful run:
Bootstrapped dev
Ubuntu 24.04.3 LTS · linux/amd64 · 8 CPUs · 31.3 GiB memory · 142.6 GiB free on /
engines: docker 28.2.2
health: healthy (observed 2026-10-04 14:05)
Next: coma connect devSupported distributions
Bootstrap installs engines on Debian and Ubuntu, with apt.
Not supported yet:
- Fedora, RHEL and other distributions without
apt. The plan fails withbootstrap_plan_failed. Install Docker or Podman yourself, then runcoma machine discover <name>so COMA records it. - Docker's own apt repository. Bootstrap uses the distribution's
docker.iopackage. - Rolling a bootstrap back.
A machine that already has the engine you ask for needs no package manager: bootstrap only checks it and creates COMA's directories.
Next
coma connect devThen continue with the Quickstart, or set up your first workspace.