-
Notifications
You must be signed in to change notification settings - Fork 0
Provisioning a Podman Host
podman-api does not run containers itself — it drives a rootless
podman.socket on each target host over SSH. This page turns a fresh Linux box
into such a target. Pick a distro from Supported host OS below first; command
examples were validated end-to-end on Debian 13 and re-validated on
AlmaLinux 10.2 (2026-06-05) — only the package step differs (dnf vs
apt).
Managed hosts must run podman ≥ 5.6.0: cold-copy migrate/evacuate streams
volumes through the libpod volume export/import API, which first shipped in
podman 5.6.0 (#85). The
daemon enforces this — it refuses to boot against a reachable host below the
floor (all offenders reported in one error), and hosts that were unreachable at
boot are version-checked on first use (operations fail with
host_version_unsupported instead of a confusing 404).
| Distro | podman (stock repos) | Supported? |
|---|---|---|
| AlmaLinux 10 (recommended) | 5.6.0 → 5.8.2 (AppStream) | ✅ |
| Rocky Linux 10 | 5.6.0 → 5.8.2 (AppStream) | ✅ |
| Fedora 41/42 | 5.6.2 / 5.8.2 | ✅ |
| Ubuntu 24.04 LTS | 4.9.3 | ❌ |
| Debian 13 (trixie) | 5.4.2 | ❌ |
| Rocky Linux 9 / RHEL 9.x | ~5.4 | ❌ |
Recommendation: AlmaLinux 10. Both RHEL-10 rebuilds ship podman 5.8.2 (exact parity with the daemon's pinned bindings); AlmaLinux wins on independent security cadence, foundation governance, and broader hardware support. Rocky 10 is a fine second choice if exact 1:1 RHEL parity is ever required.
Escape hatch (discouraged): Debian/Ubuntu can reach the floor via the upstream openSUSE OBS podman repo, but that adds per-host third-party repo maintenance we don't want on a managed fleet. Prefer reinstalling on AlmaLinux 10.
SSH in and note these — they go straight into the hosts/<id>.yaml later:
ssh debian@<host>
whoami; id -u # the user and its uid (socket path uses the uid)
cat /etc/os-release # distro/version
uname -m # arch# AlmaLinux / Rocky 10 (recommended):
sudo dnf install -y podman
# Debian/Ubuntu (unsupported on stock repos — see Supported host OS above):
sudo apt-get update
sudo apt-get install -y podman uidmap slirp4netns
podman --version # must be >= 5.6.0 (the daemon refuses older hosts)
podman info --format '{{.Host.Security.Rootless}}' # expect: trueThe socket must survive logout, so enable lingering for the user, then start
the user podman.socket:
sudo loginctl enable-linger "$USER"
export XDG_RUNTIME_DIR="/run/user/$(id -u)"
systemctl --user enable --now podman.socket
systemctl --user is-active podman.socket # active
ls -l "$XDG_RUNTIME_DIR/podman/podman.sock" # e.g. /run/user/1000/podman/podman.sock
podman ps # smoke testThe socket path is /run/user/<uid>/podman/podman.sock — record it.
If
podman pswarns about "no systemd user session", that's expected over a non-login SSH session; lingering keeps the socket alive regardless.
podman-api connects as ssh://<user>@<host><socket> using a named identity
file. Use a key the daemon host holds and the target authorises:
# on the podman-api host, confirm the key the server accepts:
ssh -v <user>@<host> true 2>&1 | grep -i "Server accepts key"Best practice is a dedicated key per host (e.g. prod-1.id_ed25519) so you
can rotate one target without touching others.
Drop a file into the directory podman-api scans with -hosts-dir:
# hosts/prod-1.yaml
id: prod-1
addr: debian@<host> # ssh://user@host (default port 22)
socket: /run/user/1000/podman/podman.sock
ssh_key: /etc/podman-api/keys/prod-1.id_ed25519
labels:
env: prod
region: ap-south-1addr: unix connects to a local socket directly (dev only). Anything else
opens an SSH tunnel using ssh_key. Real host files are git-ignored; commit
only a sanitised example.
With the daemon running (see Deploying):
curl -s -H "Authorization: Bearer $TOK" http://127.0.0.1:8080/hosts/prod-1/healthz
# {"status":"ok"}
curl -s -H "Authorization: Bearer $TOK" http://127.0.0.1:8080/hosts/prod-1
# ... "podman_version":"5.x.y" ...A successful podman_version means the SSH tunnel, identity, and socket path
are all correct. A full apply→list→delete then confirms play kube, secrets,
and port mapping — see Operating.
The -tags=integration tests use a local socket. To exercise them on this
box directly, install Go and run make test-integration there, or rely on the
podman-in-podman CI job (Building).
The optional ingress feature (-ingress-enabled, see Operating) runs a
managed Caddy pod that must publish :80 and :443 on the host. Under
rootless podman those ports are blocked until you lower the unprivileged-port
floor, persistently:
echo 'net.ipv4.ip_unprivileged_port_start=80' | \
sudo tee /etc/sysctl.d/99-podman-ingress.conf
sudo sysctl --system
sysctl net.ipv4.ip_unprivileged_port_start # => ... = 80Without it the Caddy pod fails to start (cannot expose a privileged port). Hosts that never run ingress can skip this. Running Caddy rootful is not supported — podman-api drives every host over the single rootless user socket from § 2.
For HTTP-01 to issue certs, the host's :80/:443 must be reachable from the
internet and each instance domain must already resolve to this host
(operator-managed DNS).
This is about the podman-api daemon host (where you run the binary), not the
target above. The state store is always on, and -state-db is its
location (default /var/lib/podman-api/state.db). Its parent directory must
exist and be writable by the user the daemon runs as.
The daemon creates the parent directory on startup, but a non-root run
pointed at the privileged default path will fail with a permission error. Either
run as a user that can write /var/lib/podman-api/ (the bundled installer's
podman-api system user owns it), or point -state-db at a writable path:
# example for a non-root, non-installer run:
install -d -m 0750 -o "$USER" "$HOME/.local/share/podman-api"
podman-api ... -state-db="$HOME/.local/share/podman-api/state.db"The -spec-key-file is optional — the store opens fine without it, and
templates and no-secret deploys work key-less. You only need a key to persist
secrets (secret-bearing deploys are rejected without one); see Deploying.
- Troubleshooting — SSH tunnel failures, socket path mistakes.
- Deploying — the daemon that consumes this host file.