-
Notifications
You must be signed in to change notification settings - Fork 0
Deploying
This is about installing the daemon on the host where your CMS lives. It does not need to be on the same network as the podman hosts — it only needs the SSH keys to reach them.
/etc/podman-api/
├── hosts/
│ └── prod-1.yaml # one file per target (see Provisioning a Podman Host)
├── keys.yaml # Argon2id bearer tokens
└── templates/ # optional override of the bundled set
contrib/install.sh creates a dedicated podman-api system user, builds the
binary with the correct tags (via make build), installs it to
/usr/local/bin, drops the systemd unit, and seeds an empty config tree. It
does not start the service — review config first.
sudo contrib/install.sh
# or use a pre-built binary:
sudo BINARY=/path/to/podman-api contrib/install.shGenerate an Argon2id hash with the binary itself, then add it to keys.yaml:
podman-api hash-token "my-plaintext-token"
# $argon2id$v=19$m=65536,t=3,p=4$...keys:
- id: cms-prod
secret_hash: '$argon2id$v=19$m=65536,t=3,p=4$...'
scopes: [hosts:read, instances:*, secrets:*]
description: "Prod CMS"
- id: prom-scraper
secret_hash: '$argon2id$v=19$m=65536,t=3,p=4$...'
scopes: [hosts:read]Scopes: hosts:read, hosts:write, instances:read, instances:write,
secrets:read, secrets:write (plus instances:* / secrets:* shorthands).
Rotation is live — see Operating.
podman-api \
-addr=127.0.0.1:8080 \
-metrics-addr=127.0.0.1:9090 \
-hosts-dir=/etc/podman-api/hosts \
-keys-file=/etc/podman-api/keys.yaml
# optional: -templates-dir=/etc/podman-api/templates -audit-log-file=/var/log/podman-api/audit.log
# stateful controller (migrate/evacuate): -state-db=/var/lib/podman-api/state.db -spec-key-file=/etc/podman-api/spec.key
# jobs: -jobs-retention=168h (prune old terminal jobs) -evacuate-concurrency=2 (1..32; per-request "concurrency" overrides) -job-workers=8 (background job pool size)
# migrate verify: -migrate-verify-timeout=60s (raise for slow app warm-up / healthcheck start_period) -migrate-verify-volumes=true (content-check each copied volume; =false skips it on huge volumes)Then:
systemctl enable --now podman-api
curl http://127.0.0.1:8080/healthz # {"status":"ok"}-
Binds
127.0.0.1only. The binary never exposes plaintext on a public port. TLS is the reverse proxy's job — the recommended setup is Caddy in front terminating Let's Encrypt (contrib/Caddyfile.example). -
/metricsis on a separate listener (-metrics-addr), not the main port — its labels include host/template/path and are not safe to expose publicly. Scrape it over an SSH tunnel or a VPC-internal bind. - Bearer tokens are stored as Argon2id PHC strings; plaintext exists only at issuance and in the client config.
-
Input validation (slug/template/secret/container names must match
^[a-z0-9][a-z0-9-]{0,38}[a-z0-9]$) happens at the API edge, before anything reaches the renderer or podman. -
Secrets arrive in the JSON body of
POST /instances, are written as Kubernetes Secret objects on the target host, and are zeroed in the in-memory request struct after use.
DELETE /hosts/{host}/instances/{template}/{slug} keeps volumes and
per-instance secrets by default. Pass
?prune_volumes=true&prune_secrets=true to also reap them. Delete is an
idempotent reconcile: if the pod is already gone, a delete that requests pruning
still removes orphans and returns 204. Forgetting prune_secrets leaves the
secret on the host — see Troubleshooting.
- Provisioning a Podman Host — prepare the targets first.
- Operating — day-2 concerns.