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

101 lines
5.9 KiB
Markdown

---
type: reference
title: Docker and Podman
description: Current best practices for Docker and Podman - BuildKit, multi-stage builds, rootless operation, and pods.
tags:
- docker
- podman
- containers
- infrastructure
- engineering
timestamp: 2026-08-16
verified: 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
* [Docker documentation](https://docs.docker.com/)
* [Podman documentation](https://podman.io/docs)
* [Container Specification](https://containers.dev/)
## See Also
* [Kubernetes](kubernetes.md)
* [Go](../languages/go.md)