podman-api

module
v1.0.25 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Jul 26, 2026 License: MIT

README

podman-api

A small REST wrapper around Podman's libpod REST API that lets a CMS (or any orchestrator) deploy and manage pods described by YAML templates across a fleet of Podman hosts.

It is opinionated, single-binary, and deliberately narrow: one host group, bearer-token auth, audit log to stdout, Prometheus metrics on a separate listener, kubernetes-style secrets, idempotent applies. Use it when Kubernetes is too much and ad-hoc podman run over SSH is too little.

Documentation

This README is the quick reference. The wiki is the operator's handbook:

  • Building — why the build needs tags, make targets, static/cross builds.
  • Provisioning a Podman Host — turn a fresh box into an SSH-drivable target.
  • Deploying — install the daemon: user, config tree, systemd, TLS.
  • Operating — key rotation, audit-log shipping, metrics, health checks.
  • Troubleshooting — common failures and their fixes.

What it does

  • Lists, applies, upgrades, starts/stops, and deletes pod instances on remote Podman hosts via SSH-tunneled podman.sock.
  • Renders parameterised templates (Go text/template over Kubernetes-style YAML) into Pod manifests, then plays them with podman play kube.
  • Manages per-host and per-instance secrets as Kubernetes Secret resources (so secretKeyRef works).
  • Pre-pulls every container image before play, so a bad tag fails fast with a clear registry error instead of a 30-second timeout.
  • Streams logs as plain text or SSE.
  • Migrates and evacuates instances across hosts as async jobs. Movement is cold-copy (stop → copy volumes → re-apply → verify → reap source) with rollback if the destination doesn't come up.
  • Backs up and restores instance volumes on demand: POST .../backup snapshots every volume; POST /backups/{id}/restore restores in-place with content verification. See Backing up and Restoring.
  • Exposes Prometheus metrics on a separate, opt-in listener so they aren't world-readable on the public port.

What it doesn't do

  • No multi-replica HA; one daemon, one config tree.
  • No image registry, no scheduler, no rolling deploy primitives. (Upgrade is single-pod replace-in-place.)
  • No webhook callbacks; callers poll (including the async migrate/evacuate jobs).
  • No daemon-side placement/scheduler — cross-host moves work, but the client chooses destinations (the daemon executes the move).
  • No live migration — cross-host moves are cold (stop → copy volumes → re-apply → verify → reap the source).

Architecture

   ┌────────────┐  HTTPS    ┌────────────────┐  SSH-tunneled  ┌────────────┐
   │ CMS / curl │ ───────▶  │  podman-api    │ ─────────────▶ │ podman.sock│
   │            │           │  (this binary) │   libpod REST  │  on hostN  │
   └────────────┘           └────────────────┘                └────────────┘
                              │            │
                              │            └─▶  /metrics  (separate addr, optional)
                              │
                              └─▶  audit log (stdout, JSON lines)

Hosts are declared once in hosts/*.yaml. Templates live in the store. Bearer keys live in auth/keys.yaml and reload without restart via SIGHUP.

Build

make build          # -> bin/podman-api

The podman v5 bindings require build tags to exclude CGO graph-driver headers. make build carries them. See Building for cross-compile and CI details.

Configure and run

See Deploying for the full setup guide (installer, systemd unit, TLS, bearer keys). The short version:

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 \
  -state-db=/var/lib/podman-api/state.db \
  -spec-key-file=/etc/podman-api/spec.key

A contrib/install.sh script creates a dedicated user, installs the binary, and enables the service.

When the background inventory poller is enabled (-inventory-refresh-interval, default 30s), /metrics additionally exposes per-container state rendered from the warm inventory cache:

Metric Labels Meaning
podman_api_container_restarts_total host, template, slug, container podman's restart count. Resets to 0 when the pod is recreated, so a redeploy appears as a counter reset.
podman_api_container_running host, template, slug, container 1 if the container status is running.
podman_api_container_healthy host, template, slug, container 1 healthy, 0 unhealthy or starting, -1 when the container declares no healthcheck.
podman_api_instance_ready host, template, slug 1 if every container reports a healthy or absent healthcheck.
podman_api_host_reachable host 1 if the most recent inventory refresh succeeded.
podman_api_inventory_age_seconds host Seconds since the last successful refresh.

The last two are not optional decoration. When a host becomes unreachable the cache keeps serving last-known-good data, so podman_api_container_running stays at 1 for the duration of the outage. Any alert built on the container metrics should be gated on podman_api_host_reachable == 1, with a separate alert on staleness.

Admin UI

An embedded, server-rendered admin UI (HTMX + PureCSS) is served at /ui. Disabled unless -operator-file <path> is set. Pass -ui-secure-cookie when serving over HTTPS.

Generate a password hash:

podman-api hash-token <your-password>

API reference

A complete OpenAPI 3.0 spec lives at api/openapi.yaml and is served by the binary at GET /openapi.yaml (no auth).

GET    /healthz
GET    /metrics                                                      separate listener

GET    /hosts
GET    /hosts/{host}
GET    /hosts/{host}/healthz
GET    /hosts/{host}/ports-in-use

GET    /templates
GET    /templates/{id}
GET    /templates/{id}/render?<params>
POST   /templates                            body: {id, body, ...}
PUT    /templates/{id}                       body: {body, ...}
DELETE /templates/{id}?force=
POST   /templates/{id}/clone                 body: {new_id}

GET    /hosts/{host}/secrets
PUT    /hosts/{host}/secrets/{name}          body: {"value": "..."}
DELETE /hosts/{host}/secrets/{name}

GET    /hosts/{host}/instances?template=<id>
GET    /hosts/{host}/instances/{template}/{slug}
POST   /hosts/{host}/instances
PUT    /hosts/{host}/instances/{template}/{slug}
DELETE /hosts/{host}/instances/{template}/{slug}?prune_volumes=&prune_secrets=

POST   /hosts/{host}/instances/{template}/{slug}/start
POST   /hosts/{host}/instances/{template}/{slug}/stop
POST   /hosts/{host}/instances/{template}/{slug}/restart
POST   /hosts/{host}/instances/{template}/{slug}/upgrade  body: {"image": "..."}

GET    /hosts/{host}/instances/{template}/{slug}/logs?container=&tail=&follow=
GET    /hosts/{host}/instances/{template}/{slug}/volumes
DELETE /hosts/{host}/volumes/{name}

POST   /migrate    body: {from_host, to_host, template, slug, parameters?}
POST   /evacuate   body: {from_host, map:{slug: to_host}}

POST   /hosts/{host}/instances/{template}/{slug}/backup
GET    /hosts/{host}/instances/{template}/{slug}/backups?limit=
POST   /backups/{id}/restore
DELETE /backups/{id}

GET    /jobs?state=&kind=&parent_id=
GET    /jobs/{id}

Notes:

  • POST /migrate and POST /evacuate require -state-db (else 501), validate synchronously, and return 202 {job_id}. Poll GET /jobs/{id} for progress.
  • ?skip_pull=true on POST/PUT skips the pre-pull step.
  • On DELETE, prune_volumes and prune_secrets default to false. Pass both as true to reap volumes and secrets. DELETE is idempotent — a prune-requested delete on an already-gone pod still removes orphaned volumes/secrets and returns 204.

Directories

Path Synopsis
Package apispec exposes the openapi.yaml spec embedded in the binary so it can be served at /openapi.yaml without needing the file at runtime.
Package apispec exposes the openapi.yaml spec embedded in the binary so it can be served at /openapi.yaml without needing the file at runtime.
cmd
podman-api command
Command podman-api is the HTTP service that translates CMS REST calls into libpod REST calls against one or more Podman hosts.
Command podman-api is the HTTP service that translates CMS REST calls into libpod REST calls against one or more Podman hosts.
Package extension provides public interfaces that a private commercial module can implement to extend podman-api without modifying OSS internals.
Package extension provides public interfaces that a private commercial module can implement to extend podman-api without modifying OSS internals.
internal
api
Package api wires HTTP routes to the instance.Service.
Package api wires HTTP routes to the instance.Service.
backup
Package backup holds the OSS backup primitives around instance.Service: the local-directory blob store and the backup/restore job adapters (#66).
Package backup holds the OSS backup primitives around instance.Service: the local-directory blob store and the backup/restore job adapters (#66).
evacuate
Package evacuate adapts host-evacuation orchestration to the jobs runner.
Package evacuate adapts host-evacuation orchestration to the jobs runner.
ingress
Package ingress derives a host's ingress routes from the store and pushes them to an operator-managed Caddy instance via the admin API.
Package ingress derives a host's ingress routes from the store and pushes them to an operator-managed Caddy instance via the admin API.
inventory
Package inventory keeps each host's cached instance inventory warm by refreshing it on a schedule, so UI/API reads are served without a live podman sweep and an unreachable host never stalls a request.
Package inventory keeps each host's cached instance inventory warm by refreshing it on a schedule, so UI/API reads are served without a live podman sweep and an unreachable host never stalls a request.
jobs
Package jobs runs queued jobs from a store.JobStore through registered per-kind handlers, on a bounded background worker pool.
Package jobs runs queued jobs from a store.JobStore through registered per-kind handlers, on a bounded background worker pool.
migrate
Package migrate adapts the instance migrate algorithm to the jobs runner.
Package migrate adapts the instance migrate algorithm to the jobs runner.
obs
Package obs holds the observability primitives: structured audit log middleware and Prometheus metrics.
Package obs holds the observability primitives: structured audit log middleware and Prometheus metrics.
podman/fake
Package fake is an in-memory implementation of podman.Client used by tests.
Package fake is an in-memory implementation of podman.Client used by tests.
prune
Package prune implements scheduled host-health cleanup: a "prune" job kind run by the jobs runner, fed by a per-host scheduler that fires on a configurable interval or disk high-water threshold.
Package prune implements scheduled host-health cleanup: a "prune" job kind run by the jobs runner, fed by a per-host scheduler that fires on a configurable interval or disk high-water threshold.
store
Package store is the daemon's durable desired-state record: one encrypted row per instance, written on Apply and removed on Delete.
Package store is the daemon's durable desired-state record: one encrypted row per instance, written on Apply and removed on Delete.
ui
Package ui implements the server-rendered single-operator admin UI.
Package ui implements the server-rendered single-operator admin UI.
Package templates exposes the YAML pod-spec templates shipped with the binary.
Package templates exposes the YAML pod-spec templates shipped with the binary.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL