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.
This commit is contained in:
1 parent
552d1ef8bf
commit
02883d3ed2
116 files changed
+13008
No files matched your search
@@ -0,0 +1,100 @@
|
||||
---
|
||||
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)
|
||||
Reference in new issue
Block a user