Skip to content

필사 모드: From Docker to Podman — the Complete Guide to Switching to a Daemonless Container Engine

English
0%
정확도 0%
💡 왼쪽 원문을 읽으면서 오른쪽에 따라 써보세요. Tab 키로 힌트를 받을 수 있습니다.

Introduction — Same Commands, Different Philosophy

Podman's first impression is "a tool where you swap docker for podman." Indeed, alias docker=podman makes most commands just work, and Docker CLI compatibility is an official goal. But beneath that surface the architecture is nearly the opposite — and every difference you meet during migration (config file locations, GPU setup, compose, permission issues) flows from that architectural difference.

This article organizes those differences from the root. Commands here assume Podman 5.x / Docker Engine 27+.

Internals — Daemon vs. fork-exec

Docker is client-server. When you type docker run, the CLI sends a REST request to the dockerd daemon, which delegates to containerd, which delegates to runc. Every container's ultimate parent is the daemon.

Docker:  docker CLI ──REST──▶ dockerd ──▶ containerd ──▶ runc ──▶ container
Podman:  podman CLI ──fork/exec──▶ conmon ──▶ crun(runc) ──▶ container

Podman has no daemon. podman run creates the container directly via fork-exec, like an ordinary process. Each container gets a small monitor process, conmon, managing stdio and exit codes, while actual creation is done by the OCI runtime crun (written in C — lighter and faster than runc). Implications:

  • No single point of failure. If dockerd dies (or is upgraded), all container management stalls; Podman has no central process to begin with.
  • No root daemon socket attack surface. /var/run/docker.sock is effectively root itself, and attacks targeting it are legion. In Podman that socket simply doesn't exist (a compatibility socket runs only when you want it, with user privileges).
  • Natural systemd integration. Containers are plain child processes, so systemd units can manage them directly. Modern Podman's recommended path is Quadlet — write a .container file and a systemd service is generated (podman generate systemd is now legacy).

Rootless — a Difference of Defaults

Both engines support rootless mode, but the center of gravity differs. Docker is rootful by default with rootless as opt-in; rootless is Podman's default experience (on Fedora/RHEL you use it as an ordinary user right after install).

The mechanism is user namespaces. Root (UID 0) inside the container maps to your own UID outside; other in-container UIDs map to subordinate ranges assigned in /etc/subuid and /etc/subgid. If a container escapes, all it holds is an unprivileged user's rights.

Rootless constraints to know when migrating:

  • No binding ports below 1024 (by default) — lower sysctl net.ipv4.ip_unprivileged_port_start or use high ports.
  • Networking goes through a userspace stack (pasta/slirp4netns) — plenty for most workloads; consider rootful for extreme networking needs.
  • Storage lives elsewhere — see the config section. This is where docker system df instincts get confused.
  • Slight startup overhead exists but is irrelevant for long-running services.

Config Files — What Lives Where

Docker's configuration is daemon-centric (daemon.json); Podman's is library-centric and distributed. System-wide config lives in /etc/containers/, per-user config in ~/.config/containers/ (which takes precedence).

Docker                              Podman
──────────────────────────────      ─────────────────────────────────────────
/etc/docker/daemon.json             /etc/containers/containers.conf   (engine/runtime options)
                                    ~/.config/containers/containers.conf
(registry mirrors also daemon.json) /etc/containers/registries.conf   (registries, mirrors, blocks)
                                    /etc/containers/storage.conf      (storage driver & paths)
                                    /etc/containers/policy.json       (image signature policy)
~/.docker/config.json (auth)        ~/.config/containers/auth.json    (registry auth)

Image/container storage
/var/lib/docker                     rootful:  /var/lib/containers/storage
                                    rootless: ~/.local/share/containers/storage

The two most-touched items during migration: setting unqualified-search registries in registries.conf (Docker resolves nginx to docker.io automatically; Podman by default asks or follows config — add unqualified-search-registries = ["docker.io"] to match Docker), and moving internal mirrors/auth into registries.conf + auth.json.

GPU — CDI Is the Answer

The standard path to NVIDIA GPUs in Podman is CDI (Container Device Interface) — a vendor-neutral spec describing "which device nodes, libraries, and env vars are needed to put this device into a container." It is more transparent and portable than Docker's --gpus runtime-hook approach (Kubernetes' GPU Operator uses CDI under the hood too).

# 1) Install nvidia-container-toolkit, then generate the CDI spec
sudo nvidia-ctk cdi generate --output=/etc/cdi/nvidia.yaml

# 2) Check the generated device names
nvidia-ctk cdi list
#   nvidia.com/gpu=0, nvidia.com/gpu=all, (with MIG) nvidia.com/gpu=0:0 ...

# 3) Use the GPU in a container
podman run --rm --device nvidia.com/gpu=all \
  nvidia/cuda:12.4.1-base-ubuntu22.04 nvidia-smi
  • Docker's docker run --gpus all corresponds to --device nvidia.com/gpu=all (recent Podman accepts --gpus as a compat flag, but CDI notation is canonical).
  • GPUs work rootless. The GPU driver operates in kernel space, so container privilege level doesn't affect performance. If the system-wide /etc/cdi/nvidia.yaml is readable, it's used as-is; otherwise generate the spec in user space and point Podman at that directory.
  • If the GPU disappears after a driver update, you almost certainly forgot to regenerate the CDI spec (nvidia-ctk cdi generate).

Compose — Can You Keep Your compose.yaml?

Bottom line: mostly yes, and there are two ways.

Option 1 — point the Docker Compose binary at the Podman socket (recommended). Podman can serve a Docker-API-compatible socket. With it enabled, standard docker compose works unchanged with Podman as the backend — no compose.yaml rewrite.

# Enable the user socket (rootless)
systemctl --user enable --now podman.socket

# Point docker compose at the Podman socket
export DOCKER_HOST=unix://$XDG_RUNTIME_DIR/podman/podman.sock

docker compose up -d   # backend is Podman

You're running the official Go implementation of the Compose spec, so compatibility is maximal. The podman compose command itself is a wrapper that calls an external compose provider (docker-compose if installed).

Option 2 — podman-compose. A separate project reimplemented in Python. Its virtue is purity — it invokes podman commands directly with no daemon/socket — but corners of the Compose spec (some network options, profiles, advanced depends_on conditions) can differ subtly from the reference implementation. Fine for simple stacks; for complex compose files, Option 1 is safer.

Compose-related migration notes:

  • restart: always does not mean "start on boot" in daemonless Podman — the Podman-native answer for boot autostart is Quadlet/systemd.
  • For production leanings, also consider podman kube play (run Kubernetes YAML directly) instead of compose — unifying local and Kubernetes manifests.

Migration Trap Collection — 30 Minutes If You Know, Half a Day If You Don't

  • SELinux volume labels: on Fedora/RHEL, if a volume mount hits Permission denied, it's almost certainly SELinux. Add :Z (private) or :z (shared) — -v ./data:/data:Z. The single most common trap for compose files arriving from Ubuntu.
  • Unqualified image names: without unqualified-search-registries set, podman pull nginx behaves differently from Docker. In CI scripts, the most robust fix is fully qualified paths like docker.io/library/nginx.
  • Rootless storage space: images pile up in your home directory (~/.local/share/containers). On servers with small homes, move graphroot in storage.conf.
  • Tools that assume the Docker socket (Testcontainers, some CI): mostly solved by Option 1's Podman socket + DOCKER_HOST.
  • The podman-docker package: a shim that routes the docker command to podman. Useful for script compatibility — but do tell your teammates that "docker on this server is actually podman."
  • Gradual migration works: the two engines have separate storage and can coexist on one machine. Move service by service.

Versions and Communities — Who Builds It, Where It's Discussed

Version sense (as of 2026): Podman is in its 5.x generation — the 4.x → 5.0 transition switched default networking to pasta and matured CDI support. It is deeply integrated into RHEL 9/10, and Fedora has shipped Podman as the default container tool for several releases. Docker Engine is in the 27+ generation and still owns the largest ecosystem and documentation base.

Governance and community:

  • Docker — a dual structure of Docker, Inc.'s commercial products plus open source (moby/moby). Docker Desktop requires a paid subscription beyond a certain company size (the engine itself is open source). Its greatest asset is the vast accumulation of tutorials and Stack Overflow answers.
  • Podman — part of the Red Hat-led containers GitHub organization, family to Buildah (image building) and Skopeo (image inspection/copying). All Apache-2.0 open source, and Podman Desktop is free. Discussion happens on GitHub (containers/podman), the Podman mailing list, and Matrix channels; adoption is especially strong in RHEL-family enterprise environments.

A practical picking rule: if your team runs RHEL/Fedora, requires rootless security, or prefers systemd-based operations, Podman is the natural fit. If you depend on Docker Desktop tooling or need the ecosystem's documentation mass, Docker remains the safe default. And both stand on the same OCI standard — an image built on either runs on either. Such is the blessing of having standards.

Closing

Moving from Docker to Podman is not a command swap but an operating-model swap — daemon to fork-exec, rootful to rootless, daemon.json to the containers.conf family, --gpus to CDI, compose-daemon dependence to socket compatibility or systemd/Quadlet. Thankfully, the OCI standard carries your artifacts — images and compose.yaml — across nearly untouched, and the traps converge to the few above (SELinux labels, unqualified images, storage paths). To drill container fundamentals hands-on, try this site's container lab and Linux terminal.

References

현재 단락 (1/66)

Podman's first impression is "a tool where you swap docker for podman." Indeed, `alias docker=podman...

작성 글자: 0원문 글자: 9,781작성 단락: 0/66