Remote machines
A remote nest is a machine running ant-worker (the daemon built from
cmd/agent). It accepts images, runs containers, applies compose files, and
sets up routes.
ant reaches a machine by its iroh NodeID, not by address: the address is only a label. So getting a machine running is a two-channel job, SSH once to provision, iroh forever after (no inbound ports).
Prerequisite: sign in
The daemon is fail-closed, so it needs an owner NodeID to authorize callers, and that owner must be your account. Provisioning requires a signed-in account:
ant colony signup # or: ant colony login --bundle ant-account.json
The dashboard can do this too: Settings → Ant account → Create account (or Sign in from a bundle). ant uses your account NodeID as the machine's first owner and generates the machine's own NodeID for you.
Address only (recommended)
If all you have is an address, let ant provision over SSH. It generates the
machine identity, uploads ant-worker, installs Docker and Caddy, starts the
worker as the unprivileged ant user, seeds the owner, registers the confirmed
NodeID, and you are done:
# key or SSH agent
ant nest machines add prod --host 173.87.3.89 --ssh-key ~/.ssh/id_ed25519
# password (read from stdin, never argv)
echo "$SSH_PASSWORD" | ant nest machines add prod --host 173.87.3.89 --ssh-password-stdin
ant nest ping prod
Flags: --id, --hostname, --node-id, --ssh-user (default root),
--ssh-port (default 22), --ssh-key, --ssh-password /
--ssh-password-stdin, --docker-mode (rootless default, group, proxy,
none), --no-caddy, --no-caddy-check. The host may embed the port
(--host 203.0.113.7:2222); an explicit --ssh-port wins over it.
The registry shows the machine's
own hostname (reported during provisioning); pass --hostname to override
it, the address is only a fallback.
Re-run or refresh an already-registered machine with:
ant nest bootstrap --machine prod --host 173.87.3.89 --ssh-key ~/.ssh/id_ed25519
SSH first contact is trusted (ant keeps its own ~/.ant/known_hosts), but a
changed host key is rejected. ant ships the machine identity it generated, so
the NodeID is known and pinned before any contact.
Caddy's admin API
Ant programs routes through the machine's Caddy. The upstream package exposes
that API on a root-only unix socket whose permissions vary across restarts, so
the bootstrap points it at 127.0.0.1:2019, rewriting only the admin
directive in /etc/caddy/Caddyfile (backup at /etc/caddy/Caddyfile.ant.bak)
and restarting Caddy. A host already serving that TCP address is left alone; if
the Caddyfile cannot be adjusted, ant falls back to the unix socket with a
systemd drop-in that grants the worker access.
The worker starts even when Caddy is unreachable (it warns and keeps serving management, builds, and logs); deploys are refused with a clear message until Caddy answers again.
No SSH: cloud-init
For a machine you can only reach through a provider console, register it first
(with --no-caddy-check, since the daemon is not up yet), then print a
cloud-init/user-data script. It installs the worker from --worker-url, lets
the daemon generate its own identity, and prints the NodeID to register:
ant nest machines add prod --hostname prod.example.com --no-caddy-check
ant nest bootstrap --machine prod --cloud-init --worker-url https://ants-docs.pages.dev/dl/ant/v0.1.0/ant-worker-linux-amd64
# paste the output as user-data; then read the NodeID from the console log:
ant nest machines add prod --hostname prod.example.com --node-id <id>
--worker-url is required with --cloud-init: the target must fetch the
worker binary directly, so pass the ant-worker-linux-<arch> URL from a
release (or a URL you host). Prefer
ant nest bootstrap --machine prod --host <address>, which uploads the locally
built worker and needs no hosting.
Already running a worker
If ant-worker is already installed and running (say, out of band), just
register its NodeID:
ant nest machines add prod --hostname prod.example.com --node-id 7f2e91ac…
Inspecting and operating a machine
ant nest ping prod # reachable? (--timeout, default 20s)
ant nest audit --machine prod # tamper-evident action log (--limit, --json)
ant nest state export --machine prod --out prod-state.json
ant nest state import --machine prod --file prod-state.json # owner-only
ant nest identity show --machine prod # the machine's NodeID
ant nest tunnel --machine prod --container app --port 8080 # forward over iroh
ant nest machines network prod --name ant-prod --driver bridge # machine-wide network
ant nest groups network backend --name backend-net # a group's network
ant nest audit reports whether the hash chain verifies; ant nest state
backs up and restores the roster and invites (import refuses a document with no
users unless --force).
Manual plan
ant nest bootstrap with no address prints the root-run steps for an operator to
apply by hand; the same script ant runs over SSH, plus the Docker/Caddy steps.
See it for any machine with ant nest bootstrap --machine prod.
Add users
ant colony invite --role deployer --machine prod
# on the other side:
ant colony join --token ant_inv_…
Invites are signed, machine-pinned, single-use, and redeemed server-side. See Colony & users.
No inbound ports
Because iroh dials by NodeID and uses a relay for hole-punching, the machine does not need an open inbound port. SSH is used only during provisioning; the steady-state control plane is iroh.
Dashboard
The same flow is on the Nest page: Add machine has an Already running mode (paste the NodeID) and a Provision over SSH mode (address and credentials, streamed progress). Each machine's Setup tab shows its NodeID and the bootstrap plan, and can re-provision. See the Dashboard page.
Removing ant from a machine
ant nest machines remove <id> only drops the local registry record. To reverse
provisioning on the machine itself (stop and delete ant-worker, its systemd
unit, its binary, and the Caddy admin drop-in ant installed) decommission it
over SSH:
ant nest machines decommission prod --host 173.87.3.89 --ssh-key ~/.ssh/id_ed25519 --yes
ant nest machines decommission prod --host 173.87.3.89 --ssh-password-stdin --purge --yes
ant nest machines decommission prod --host 173.87.3.89 --ssh-key … --yes --keep-record
Decommission is an SSH operation, so --host (plus --ssh-user/--ssh-port/
--ssh-key or --ssh-password-stdin) is required, along with --yes. The
host may carry the port as host:port (e.g. --host 203.0.113.7:2222).
--purge also removes the worker user and its state (the machine identity and
roster), every ant-managed container and image, and the networks ant created for
it, leaving the host as it was before provisioning. Without --purge, the
machine keeps /home/ant, so a later provision reuses the same identity and
roster and your projects' domains keep resolving to the same NodeID.
--keep-record leaves the local registry entry in place.
Leaving ant without stopping the app
Without --purge, the ant-managed containers keep running after the worker is
gone. Add --handover to also export a plain Docker takeover bundle to the SSH
user's home (~/ant-handover/):
ant nest machines decommission prod --host 173.87.3.89 --ssh-key ~/.ssh/id_ed25519 --handover --yes
The bundle contains a README.md, a compose/<project>/docker-compose.yml for
every remote compose project (plus its secrets.env, 0600, when it has one),
a synthesized containers/<app>/docker-compose.yml for every standalone
container deploy, and caddy/Caddyfile.ant-handover, the routes ant
programmed, rendered as Caddyfile blocks (one per domain, with the upstream
ports) so the domains keep working once ant is gone. The files carry no ant
labels, so you can take the app over with plain Docker:
docker ps -a --filter "label=computer.ants.managed=1" --format '{{.Names}}' | xargs -r docker rm -f
docker compose -f containers/<app>/docker-compose.yml up -d
The running containers stay up until you remove them, so the transition is
reversible until you delete them. --handover cannot be combined with
--purge (purge deletes the workloads it would export).