Remote

Remote machines

Register a machine, provision the worker, and authenticate.

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.

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).

Next

Copyright © 2026