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.
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. |
|
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
USERto a non-root user. - Use
ENTRYPOINTin exec form. - Add a
HEALTHCHECK. - Pin base image digests.
- Use a
.dockerignorefile.
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) hasdns_enabled: false. Container names do not resolve on it. Create a quadlet.networkfile (DNS is enabled by default for quadlet networks) and point the containers at it. ${VAR}expansion in the generatedExecStartneeds the variable in the systemd unit environment. PutEnvironmentFile=/path/to/envin the[Service]section of the quadlet file. AnEnvironmentFile=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 withsystemctl --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 injournalctl --user, and the unit simply never appears insystemctl list-unit-files). Use the podman-flag names:AddCapability=(notCapAdd=),AddDevice=HOST:CONTAINER(notDevice=— that is a[Volume]key). When a unit mysteriously does not generate, read the journal forquadlet-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.targetin the.containerfile is the enablement —systemctl is-enabledreportsgenerated. With user linger on, they start at boot. Justdaemon-reloadthenstart. - A
.networkunit is also generated and cannot be enabled. It is pulled in automatically by any container that setsWants=/After=on it (orNetwork=<name>.network). - To make one container reuse another's network namespace (the compose
network_mode: service:Xequivalent), setNetwork=<name>.containeron 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.networkfile (maps topodman network create --subnet). Rootless podman otherwise reassigns the CIDR, which breaks anytrusted_proxiesentry 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 onNetwork=<vpn>.container. Secrets live in a mode-0600 env file referenced byEnvironmentFile=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.