CI
Ant is a single binary, so a CI job is three steps: install it, sign in with a
ci-role account, and deploy. Nothing prompts, and every failure exits
non-zero.
One-time setup
- Invite the pipeline's account with the
cirole.cican deploy, roll back, and read status and logs; it cannot change the roster, read the audit log, or hold OS accounts.ant colony invite --role ci --machine prod # or, once the account's NodeID is known: ant colony users add <nodeid> --machine prod --role ci
The pipeline needs its own account identity: sign up once, redeem the invite, then export it.ant colony signup --name ci ant colony join --token "$(cat invite.txt)" --alias prod ant colony export --out ant-ci.json - Store the bundle and passphrase as CI secrets: the bundle file as
ANT_BUNDLEand its passphrase asANT_PASSPHRASE(masked). - Register the machine so the runner can dial it. The NodeID comes from
ant nest bootstrap's output orant nest identity showon the machine:ant nest machines add prod --hostname prod.example.com --node-id <nodeid>
GitHub Actions
name: Deploy
on:
push:
branches: [main]
jobs:
deploy:
runs-on: ubuntu-latest
env:
ANT_MACHINE: prod
ANT_PASSPHRASE: ${{ secrets.ANT_PASSPHRASE }}
steps:
- uses: actions/checkout@v6
- name: Install ant
run: |
curl -fsSL https://ants-docs.pages.dev/dl/install.sh | sh
echo "$HOME/.local/bin" >> "$GITHUB_PATH"
- name: Sign in
run: |
printf '%s' "${{ secrets.ANT_BUNDLE }}" > /tmp/ant-ci.json
ant colony login --bundle /tmp/ant-ci.json
- name: Register the machine
run: ant nest machines add prod --node-id "${{ vars.ANT_MACHINE_NODE_ID }}"
- name: Deploy
run: ant trail deploy --release "$GITHUB_SHA" --json | tee deploy.json
GitLab CI
deploy:
stage: deploy
image: docker:27
services: [docker:27-dind]
variables:
ANT_MACHINE: prod
script:
- apk add --no-cache curl
- curl -fsSL https://ants-docs.pages.dev/dl/install.sh | sh
- export PATH="$HOME/.local/bin:$PATH"
- printf '%s' "$ANT_BUNDLE" > /tmp/ant-ci.json
- ant colony login --bundle /tmp/ant-ci.json
- ant nest machines add prod --node-id "$ANT_MACHINE_NODE_ID"
- ant trail deploy --release "$CI_COMMIT_SHA" --json
only: [main]
ANT_PASSPHRASE, ANT_BUNDLE, and ANT_MACHINE_NODE_ID are masked CI
variables. The install script and the release downloads need no credentials.
A runner with Docker is only needed for source builds; a project with
deploy.build: none (or the --image form) builds nothing locally.
Split build and deploy
Build once in a fast job, push to the registry, and deploy in another job that needs neither the source nor a local build:
# job A, needs deploy.registry in ant.yaml and ANT_REGISTRY_PASSWORD
ant haul --push --release "$GITHUB_SHA"
# job B, runs the pushed image on the machine; set ANT_REGISTRY_USERNAME and
# ANT_REGISTRY_PASSWORD here too, and the pull credential travels with the deploy
ant trail deploy --image ghcr.io/org/app:"$GITHUB_SHA" --pull --machine prod
For a private image, job B either sets ANT_REGISTRY_USERNAME +
ANT_REGISTRY_PASSWORD (the credential rides the deploy to the machine and is
gone when it finishes) or the machine carries its own docker login.
ant haul --push always builds on the runner (the image must exist there to be
pushed) and supports container projects; compose projects push their service
images with docker.
Reading the result
--json prints the deploy or rollback result on stdout while build output
stays on stderr, so a pipeline can consume it directly:
release=$(ant trail deploy --json | jq -r .release)
ant trail rollback --to "$release" --json
Fields: app, deployment, machine, runner, builder, image,
release, container, compose, routes, warnings.
Rollback from CI
ant trail rollback --to "$PREVIOUS_RELEASE" --json
rollback.keep_releases (or deploy.keep_images) controls how many releases
stay on the machine; the target image tag must still exist there.
Notes
- The runner needs a Linux amd64/arm64 build with CGO; the release binaries are. The macOS/Windows CLI builds run local-first and cannot reach a machine.
ANT_MACHINEis the environment fallback for--machine, so a pipeline sets the target once.- The bundle is the account key. Scope it with the
cirole per machine, keep it in a secret store, and rotate by removing the member and issuing a new invite. - Worker and CLI must speak the same protocol version; upgrade the machine with
ant nest bootstrapwhen the CLI reports a mismatch.