Files
command-center/omp-docs/engineering/infra/containers.md
T
Frank 02883d3ed2 docs: rebuild KB in OKF v0.1 with live-verified facts
Copy of the legacy ~/.pi/agent/docs tree rebuilt as the command-center
knowledge base: concept files promoted to path identity, index listings
stripped of frontmatter, all wikilinks/relative links resolved, every
checkable claim verified against the live fleet (Dankserver = Ryzen 7
7700X + RTX 3090; Strix Halo is Dankclaw), unverifiable claims tagged.
kb-lint exits 0.
2026-08-28 07:57:13 -07:00

5.9 KiB

type, title, description, tags, timestamp, verified
type title description tags timestamp verified
reference Docker and Podman Current best practices for Docker and Podman - BuildKit, multi-stage builds, rootless operation, and pods.
docker
podman
containers
infrastructure
engineering
2026-08-16 2026-08-28

Docker and Podman

This page covers current (2025/2026) best practices for containers.

Docker

Use BuildKit for builds. It enables incremental builds, cache mounts, and secret mounts.

Use multi-stage builds for production images. Build in a full stage with the toolchain. Copy only the artifacts to a minimal final stage. Name each stage with AS.

Harden images with these practices:

  • Use distroless or alpine base images.
  • Set USER to a non-root user.
  • Use ENTRYPOINT in exec form.
  • Add a HEALTHCHECK.
  • Pin base image digests.
  • Use a .dockerignore file.

Use Docker Compose v2 for multi-container applications. Run docker compose without a hyphen.

Podman

Podman runs without a daemon and rootless by default. Run it as a non-root user. An alias alias docker=podman works for most commands.

Use Podman pods to group containers that share network and IPC namespaces. Pods suit single-host service groups.

Use Quadlet for container lifecycle management under systemd. podman generate systemd is deprecated as of Podman 5.7; the deprecation notice points to Quadlet.

Podman is a good choice for Linux servers, homelabs, and CI runners.

Quadlet (systemd-managed containers)

Quadlet files are systemd unit files with a podman section ([Container], [Network], etc.). Podman ships a systemd generator that turns them into container services.

Rootless search path: ~/.config/containers/systemd/. System-wide: /etc/containers/systemd/.

Fact Value
Unit naming (podman 5.x) foo.container → foo.service (no container- prefix)
Container naming ContainerName=foo or default systemd-foo
Boot start [Install] WantedBy=default.target in the file; the generator applies it
Rootless boot start enable user-level linger: loginctl enable-linger <user>
Start behavior podman run --name ... --replace --rm; stop removes the container
Cross-container deps [Unit] Wants= / After= referencing other quadlet unit names
Network Network=foo.network uses a podman network created from foo.network
Long pulls add [Service] TimeoutStartSec=900

Reference: man podman-systemd.unit (podman-systemd.unit(5)).

Gotchas found in the field (2026-08-16, Gitea migration on dankclaw)

  • The automatic rootless default network (podman) has dns_enabled: false. Container names do not resolve on it. Create a quadlet .network file (DNS is enabled by default for quadlet networks) and point the containers at it.
  • ${VAR} expansion in the generated ExecStart needs the variable in the systemd unit environment. Put EnvironmentFile=/path/to/env in the [Service] section of the quadlet file. An EnvironmentFile= in [Container] only lands in the ignored [X-Container] section and does not feed expansion.
  • A podman restart (or systemctl --user daemon-reload) re-runs the generator after quadlet file edits. Verify the parsed unit with systemctl --user cat <unit>.
  • Bind-mounted files created by the container are owned by the mapped host UID (container uid 1000 → first subuid + 1000). Edit them with podman exec --user 1000 <container> ....

Gotchas found in the field (2026-08-19, SearXNG trio migration on dankclaw)

  • The quadlet generator rejects several compose-familiar keys in [Container] and silently skips the whole unit (the error is only in journalctl --user, and the unit simply never appears in systemctl list-unit-files). Use the podman-flag names: AddCapability= (not CapAdd=), AddDevice=HOST:CONTAINER (not Device= — that is a [Volume] key). When a unit mysteriously does not generate, read the journal for quadlet-generator: converting "...": unsupported key.
  • Quadlet units are generated units (they live in /run/user/UID/systemd/generator/). systemctl enable <unit> fails on them with "transient or generated". You do not enable them: the [Install] WantedBy=default.target in the .container file is the enablement — systemctl is-enabled reports generated. With user linger on, they start at boot. Just daemon-reload then start.
  • A .network unit is also generated and cannot be enabled. It is pulled in automatically by any container that sets Wants=/After= on it (or Network=<name>.network).
  • To make one container reuse another's network namespace (the compose network_mode: service:X equivalent), set Network=<name>.container on the consumer. The generator adds the dependency on <name>.service. This is how SearXNG exits the Gluetun WireGuard tunnel: Network=gluetun.container.
  • Pin a quadlet network CIDR with Subnet=<cidr> in the .network file (maps to podman network create --subnet). Rootless podman otherwise reassigns the CIDR, which breaks any trusted_proxies entry that references it.
  • A working pattern for a boot-persistent trio: a .network (DNS on, Subnet= pinned) that the VPN client and the cache join, plus the consumer container on Network=<vpn>.container. Secrets live in a mode-0600 env file referenced by EnvironmentFile= in each unit's [Service].

Docker vs Podman

Factor Docker Podman
Platform Mac and Windows Linux focus
Model Daemon-based Rootless and daemonless
Default user Root Non-root
Grouping Compose services Pods

Use Docker for cross-platform development. Use Podman rootless for Linux servers and security-conscious deployments.

Citations

See Also