4  Containers and Pods

Note

Rootless Podman with quadlet/systemd. One service user per application. Every app is a pod.

4.1 Why Podman

  • No daemon, no root socket, no single point of failure for all containers
  • Podman runs rootless by default: each container runs under a regular user’s UID namespace
  • OCI compatible — same images, same Containerfiles, same registries as Docker
  • First-class systemd integration via quadlet

4.2 One user per service

  • Each service gets a dedicated system user with a static UID (2000–2013) and its own home directory
  • Quadlet files live in ~/.config/containers/systemd/ per user
  • loginctl enable-linger keeps the user’s systemd instance running without an active session
  • Blast radius: a compromised container only has access to one service user’s files and network namespace

4.3 Quadlet and systemd

  • Quadlet translates .container, .pod, .network files into transient systemd units
  • Dependencies, ordering, restart policy, and journal logging come from systemd, not a container orchestrator
  • systemctl --user daemon-reload picks up changes; systemctl --user restart <pod> applies them
  • No docker-compose.yml, no separate daemon to manage

4.4 The pod model

  • Every service is a Podman pod: one or more application containers plus a Caddy sidecar for TLS
  • Containers in a pod share localhost; the Caddy sidecar reverse-proxies to the app on 127.0.0.1
  • The pod publishes a single port on the host’s loopback (e.g., 127.0.0.1:8440→443)
  • Pod files (.pod) declare RequiresMountsFor= against ZFS datasets so systemd orders startup correctly

4.5 What goes in a pod

  • Typical pod: app server + Caddy sidecar
  • Some services add more: Authentik has server + worker + PostgreSQL + Caddy; Immich has server + ML + PostgreSQL + Caddy
  • Databases run in-pod rather than shared: each service owns its own PostgreSQL/MariaDB, backed by its own ZFS dataset

4.6 Health checks

  • Every .container file defines a health check against the application’s HTTP health endpoint
  • Caddy sidecars health-check against their admin API
  • systemd restarts unhealthy containers per the restart policy

4.7 Auto-update with :latest

  • All service containers pull :latest (or :release) tags
  • podman auto-update checks registries for new digests and restarts containers that have changed
  • This is a deliberate tradeoff: latest-tag updates are simple and automatic, but break reproducibility; ZFS snapshots before deploy provide the rollback path