TT Lab
Get started
Learn Learning paths Courses

Running Rootless Podman

A Map of podman's Configuration Files

Continue in TT Lab

In one line

If docker's configuration lives in a single daemon.json, podman uses several files split by role, and per-user configuration overrides system configuration.

Why this was needed

This is the first place where people coming from docker get lost. "Where do I put the registry mirror?" "Where do I change the storage path?" There is no daemon, so there is no daemon.json either.

How it works

docker                          podman
──────────────────────────      ───────────────────────────────────────
/etc/docker/daemon.json         /etc/containers/containers.conf   (엔진·런타임 옵션)
                                ~/.config/containers/containers.conf
(레지스트리 미러도 daemon.json)  /etc/containers/registries.conf   (레지스트리·미러·차단)
                                ~/.config/containers/registries.conf
                                /etc/containers/storage.conf      (스토리지 드라이버·경로)
                                ~/.config/containers/storage.conf
                                /etc/containers/policy.json       (이미지 서명 정책)
~/.docker/config.json (인증)     ~/.config/containers/auth.json    (레지스트리 인증)

이미지·컨테이너 저장 위치
/var/lib/docker                 rootful:  /var/lib/containers/storage
                                rootless: ~/.local/share/containers/storage

The per-user file overrides the system file. That is why most rootless configuration happens under ~/.config/containers/.

storage.conf

[storage]
driver = "overlay"
runroot = "/run/user/1000/containers"
graphroot = "/home/podster/.local/share/containers/storage"

[storage.options]
additionalimagestores = []

[storage.options.overlay]
mount_program = "/usr/bin/fuse-overlayfs"

registries.conf

unqualified-search-registries = ["docker.io"]

[[registry]]
prefix = "docker.io"
location = "mirror.internal.example.com/dockerhub"

[[registry]]
location = "blocked.example.com"
blocked = true

unqualified-search-registries is the typical difference from docker. docker automatically resolves nginx to docker.io, but podman follows the configuration or asks which registry you mean. In a CI script, the most robust approach is to use the full path, such as docker.io/library/nginx.

containers.conf

It sets engine defaults: the default OCI runtime (crun/runc), the default network backend (netavark/cni), the default cgroup manager, default environment variables, and so on.

[engine]
runtime = "crun"
cgroup_manager = "cgroupfs"

[containers]
default_ulimits = ["nofile=65535:65535"]

If rootless has no systemd or cgroup delegation is unavailable, you must set cgroup_manager = "cgroupfs".

What it looks like in the field

The initial setup checklist. When you set up rootless podman on a new server, there is a fixed order of things to check.

  1. Is there a dedicated user with a home directory?
  2. Do /etc/subuid and /etc/subgid contain a range for that user?
  3. Are newuidmap/newgidmap installed?
  4. Is graphroot on a partition with enough space?
  5. Is runroot on tmpfs?
  6. Does registries.conf have the internal mirror and the unqualified search setting?
  7. In podman info, are rootless=true, the driver, and the paths as expected?

Item 7 is the final verification. Many teams parse podman info --format json and check it automatically in CI.

What you will do in the next lab

You add subuid/subgid for the user podster, calculate the mapping by hand, write storage.conf and registries.conf, and verify everything with podman info.