Deploying a fleet¶
punix service deploy deploys one stack to one target. punix fleet apply deploys many stacks across many hosts from one command — orchestration over the existing per-host deploy, not a new mechanism. Each host's deploy stays atomic and independently rollback-able.
The Fleet module¶
# fleet.pcl
module Fleet {
hosts = [
{ file = "hosts/web.pcl" stack = "WebStack" target = "ssh://deploy@web.example.com" }
{ file = "hosts/db.pcl" stack = "DbStack" target = "ssh://deploy@db.example.com" }
{ file = "hosts/worker.pcl" stack = "WorkerStack" target = "ssh://deploy@worker.example.com" }
]
}
Each host names a self-contained PCL file, the stack module inside it, and the deploy target. Host file paths are resolved relative to the fleet file.
Failure isolation¶
By default a failing host is recorded and the run continues; the command exits 1 if any host failed, with a per-host report. --fail-fast stops at the first failure. Each host's result is one of four statuses: ok, failed (its own deploy raised), blocked (a host it depends on — via a cross-host artifact — failed or was blocked, so it is skipped without being attempted), or not-attempted (under --fail-fast, never reached).
host web.example.com → deployed: gen-004
host db.example.com → FAILED: [E11] secret(s) not set: from_vault:db_pw
host worker.example.com → deployed: gen-002
Flags¶
fleet apply takes the fleet file as a positional argument, plus:
--fail-fast— stop at the first failing host (default: continue).--dry-run—RealiseDryRunfor every host (no real builds or writes).--key/--known-hosts— SSH credentials forssh://targets.--tls-certs DIR/--vault-secrets FILE/--activate— same asservice deploy, applied per host.--transport-root PATH— hermetic: deploy each host to<root>/<target>/viaLocalTransportinstead of SSH (for tests and local runs).--scenario NAME.
Cross-host wiring is config, not references¶
PCL has no imports, so each host is a separate evaluation — there is no fleet-wide reference resolution. A service on one host reaches another by address or secret (its config), never by a cross-module PCL reference. Don't expect WebStack to read a binding from DbStack.
Deploy ordering & cross-host artifacts¶
The one cross-host coupling that isn't a static address is a deploy-time artifact — a fact that only exists after a host deploys (a cert digest for DANE/TLSA, a DKIM key). A host producesArtifacts; another consumes them via an { artifact = { host, name } } reference. fleet apply builds the producer→consumer graph up front, deploys producers before consumers (stable topological order; a cycle is rejected before any host runs), and threads only the digest across — never the preimage. See Cross-host deploy-time artifacts for the full recipe.
Where in the code¶
src/punix/deploy/fleet.py—read_fleet,apply_fleet(pure orchestration, no Transport).src/punix/cli.py— thefleet applycommand wires it to the per-host deploy.
Related¶
punix fleet— the full CLI reference (apply+rollback).- Cross-host deploy-time artifacts — DANE/DKIM ordering (ADR-022).
- Config safety — what a host's
configFilesmay write. punix service— the per-host command.- Deploy over SSH · Activation.
- Reference: decisions — ADR-012.