Deploying a fleet¶
punix service deploy deploys one stack to one target. punix fleet apply deploys many stacks across many hosts from one command. It orchestrates over the same per-host deploy path, so 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: deploy each host to<root>/<target>/viaLocalTransportinstead of SSH (for tests and local runs).--scenario NAME.
Cross-host wiring through config¶
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. The preimage never crosses the wire. 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/fleet.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.