Install ztd
ztd is a single static binary. It carries its own runtime assets — the Terraform
root, the lima template, cloud-init, the toolbox compose file — embedded inside
it, so there is no ZTD source checkout to clone and nothing to keep in sync
with the binary.
What it still needs from the host is the thing that can’t be shipped in a binary:
| Backend | Host needs |
|---|---|
vz (local macOS) | Docker + lima |
kvm (local Linux) | Docker + libvirt/qemu/dnsmasq + virtiofsd |
proxmox / ec2 (remote) | Docker only |
Run ztd check after installing — it
verifies every one of those and tells you the exact command to fix what’s missing.
macOS — Homebrew
The tap lives on GitLab, so it is added by URL:
brew tap frob/ztd https://gitlab.com/frob/homebrew-ztd.git
brew install --cask ztd--cask, not a formula: Homebrew formulae are for building from source, and
shipping a pre-built binary through one is deprecated upstream. The cask pulls in
lima as a dependency (it is the hypervisor for the vz backend) and strips
the Gatekeeper quarantine attribute at install time, so the first run doesn’t die
with “cannot be opened because the developer cannot be verified”.
Docker is the other host requirement and is deliberately not a cask dependency —
Docker Desktop, Colima or another runtime is your call. Install one before
ztd up.
Upgrade and remove the usual way:
brew upgrade --cask ztd
brew uninstall --cask ztdLinux — download the binary
Grab the archive for your architecture from the
releases page, verify it, and drop the
binary on your PATH:
VERSION=0.4.3 # the release you want
ARCH=amd64 # or arm64
base="https://gitlab.com/frob/ztd/-/releases/$VERSION/downloads"
curl -fLO "$base/ztd_${VERSION}_linux_${ARCH}.tar.gz"
curl -fLO "$base/checksums.txt"
sha256sum --check --ignore-missing checksums.txt
tar -xzf "ztd_${VERSION}_linux_${ARCH}.tar.gz" ztd
install -Dm755 ztd ~/.local/bin/ztdMake sure ~/.local/bin is on your PATH. To upgrade, repeat with a newer
VERSION; to uninstall, rm ~/.local/bin/ztd (and see Where ztd puts things
below).
From source
You need Go and Docker:
git clone https://gitlab.com/frob/ztd.git && cd ztd
task go:build # -> bin/ztd
task install:ztd # symlinks bin/ztd onto $PATH (PREFIX=~/.local/bin)Note the difference between the two entrypoints in a checkout: ./ztd is the
dev wrapper, which points the binary back at the live source tree so script
edits take effect immediately. The installed binary uses its embedded assets.
When you want to test what a user gets, use the installed binary from a directory
that is not the checkout — see CONTRIBUTING.
First run
From whatever directory holds the work you want an agent to touch:
ztd check # host preflight — fix anything it flags
ztd setup # local kvm only: applies the two host-side fixes
ztd init # writes .ztd/ztd.toml (all keys commented; defaults boot)
ztd up # boot a VM
ztd ssh
ztd downztd init also writes a .ztd/.gitignore so the ephemeral key, any API tokens,
Terraform state and agent transcripts can’t be committed. ztd.toml itself is
meant to be committed — it holds no secrets.
The backend defaults to local, which resolves per-platform: vz on macOS,
kvm on Linux. Set backend in .ztd/ztd.toml (or TARGET= in the
environment) for proxmox/ec2.
Where ztd puts things
| Path | What | Safe to delete |
|---|---|---|
<project>/.ztd/ | your config, ephemeral key, Terraform state, run records | only after ztd down |
~/.cache/ztd/<version>/ (macOS: ~/Library/Caches/ztd) | the extracted embedded assets for that version | yes — re-extracted on next run |
The cache is version-scoped and re-created on demand, so an upgrade never leaves a stale asset tree behind and nothing you care about lives there.
Verifying what you installed
ztd version
ztd config # includes the toolbox image this binary will pullA released binary pins the toolbox container image to its own version — the
ztd config output should read registry.gitlab.com/frob/ztd/ztd-toolbox:<that version>. A dev/snapshot build says ztd-toolbox:local and builds the image
locally instead. See
Pin or override the toolbox image.