Chapter 68: Podman in Depth: Install, Rootless Containers, Pods, Compose, Kube YAML and Alternatives
68.1 Podman install and architecture: daemonless containers on Linux, macOS and Windows
Why: the problem
Podman is a container engine from Red Hat that runs the same OCI images as Docker, uses almost the same commands, and has one big design difference: there is no daemon. When you type docker run, the docker CLI sends a request to dockerd, a long-running root process that owns every container on the machine. When you type podman run, the podman process itself sets up the container with conmon and the OCI runtime crun, and then exits, leaving the container as a normal child process supervised by conmon. Nothing runs in the background when no containers are running. Why does that matter? A root daemon is a single point of failure and a large security target. If dockerd crashes or is upgraded, all its containers are affected, and anyone who can talk to the docker socket is effectively root on the host. Podman removes both problems: each container belongs to the user who started it, upgrades of the podman package do not touch running containers, and there is no socket to abuse unless you enable one on purpose. That is why Red Hat Enterprise Linux 8 and later ship Podman instead of Docker, and why banks, telecoms and government teams in India running RHEL standardise on it. What do you get? The podman CLI with docker-compatible commands, the libpod library that manages containers, pods, images and volumes, crun or runc as the low-level runtime, netavark and aardvark-dns for networking, pasta for rootless networking, and containers-storage for images. Images are shared with Buildah and Skopeo, which use the same storage. Podman can also run pods, groups of containers that share a network like Kubernetes pods, which we cover in 68.3. How do we install it? On Fedora and RHEL it is one dnf command. On Ubuntu 24.04 it is in the standard apt repositories. On macOS and Windows, containers need a Linux kernel, so podman machine creates a small Fedora CoreOS virtual machine and the CLI on your laptop talks to it, the same idea as Docker Desktop. Podman Desktop is the free graphical app on top. We install it, check versions and paths, run a first container, and prove there is no daemon.
What: every term in plain English
- Podman — Daemonless OCI container engine with a docker-compatible CLI.
- libpod — Library inside Podman that manages containers, pods, images and volumes.
- conmon — Small monitor process that keeps each container's stdio and exit code.
- crun — Fast OCI runtime written in C; runc is the alternative.
- netavark — Podman's network stack for rootful and bridge networks.
- aardvark-dns — DNS server that lets containers on a network find each other by name.
- pasta — User-mode networking for rootless containers, default since Podman 5.
- containers-storage — Image and layer store shared with Buildah and Skopeo.
- podman machine — Linux VM used on macOS and Windows.
- Podman Desktop — Graphical app for Podman, Kubernetes and extensions.
How: step by step
- Install Podman on your OS. podman command works.
- Check version and info. Runtime and storage shown.
- On macOS or Windows, init and start a machine. VM running.
- Pull and run a first container. nginx answers.
- Look at the process tree. conmon, no daemon.
- Stop and start the container. Same commands as Docker.
- Compare with docker ps on the same host. Separate stores.
- Clean up. Nothing left running.
A simple picture helps when someone asks you to explain the difference on a whiteboard. Draw Docker as three boxes in a line: docker CLI, then dockerd running as root, then containerd and runc, with every container hanging under that one daemon. Draw Podman as two boxes: the podman CLI, which does the work and exits, and next to each container its own conmon process owned by the user who started it. Then add one sentence: in Docker, whoever controls the daemon controls the host; in Podman, each user controls only their own containers. Infosys and TCS teams who support RHEL customers use exactly this drawing in onboarding sessions, because it explains in one minute why the RHEL security team prefers Podman and why most Docker commands still feel the same.
Example 1: Install on Linux, macOS and Windows
Goal. Install on Linux, macOS and Windows.
install.sh
# Fedora / RHEL 9 / Rocky / AlmaLinux
sudo dnf install -y podman
# Ubuntu 24.04 / Debian 13
sudo apt-get update && sudo apt-get install -y podman
# macOS (Homebrew), then a VM
brew install podman
podman machine init --cpus 2 --memory 4096
podman machine start
# Windows (PowerShell, WSL 2 backend)
# winget install RedHat.Podman
# podman machine init ; podman machine start
podman version
podman info --format 'runtime={{.Host.OCIRuntime.Name}} storage={{.Store.GraphDriverName}} rootless={{.Host.Security.Rootless}} net={{.Host.NetworkBackend}}'
What each line does
dnf install podman— On RHEL family systems Podman comes from the default repositories, already supported by Red Hat.apt-get install podman— Ubuntu 24.04 ships Podman 4.9; newer Ubuntu and Fedora releases ship Podman 5 or 6.podman machine init— Creates a Fedora CoreOS VM; --cpus and --memory size it.winget— Windows installer; the machine uses WSL 2 or Hyper-V.podman info --format— Shows the runtime, storage driver, rootless flag and network backend in one line.
Green output
$ podman version
Client: Podman Engine
Version: 6.1.4
API Version: 6.1.4
Go Version: go1.25.3
OS/Arch: linux/amd64
$ podman info --format '...'
runtime=crun storage=overlay rootless=true net=netavark
Expected result. One package and Podman is ready. On Linux there is no service to enable, which is the first visible sign of the daemonless design.
Example 2: First container and proof there is no daemon
Goal. First container and proof there is no daemon.
first.sh
podman run -d --name web -p 8080:80 docker.io/library/nginx:1.27-alpine
curl -s localhost:8080 | grep -o '<title>.*</title>'
podman ps --format '{{.Names}} {{.Image}} {{.Status}} {{.Ports}}'
ps -o pid,ppid,user,cmd -C conmon,nginx | head -5
systemctl status podman 2>/dev/null | head -2 || true
pgrep -a dockerd || printf 'no dockerd\n'
podman stop web && podman start web
podman logs --tail 2 web
What each line does
Fully qualified image name— docker.io/library/nginx avoids the short-name prompt; Podman does not assume Docker Hub.-p 8080:80— Same port syntax as Docker; rootless ports above 1024 need no privileges.ps -C conmon— conmon runs as your user and is the parent of nginx; no daemon is involved.systemctl status podman— Only an optional API socket service exists, normally inactive.stop, start, logs— Muscle memory from Docker works unchanged.
Green output
$ bash first.sh
Trying to pull docker.io/library/nginx:1.27-alpine...
Writing manifest to image destination
3e1a9f...
<title>Welcome to nginx!</title>
web docker.io/library/nginx:1.27-alpine Up 2 seconds 0.0.0.0:8080->80/tcp
PID PPID USER CMD
41210 1 ravi /usr/bin/conmon --api-version 1 -c 3e1a9f... -u 3e1a9f...
41212 41210 ravi nginx: master process nginx -g daemon off;
no dockerd
web
web
2026/10/25 03:30:11 [notice] 1#1: start worker processes
Expected result. The container runs under conmon as user ravi, with no daemon anywhere. If you close the terminal or upgrade Podman, the container keeps running.
Example 3: Docker compatibility: alias, socket and short names
Goal. Docker compatibility: alias, socket and short names.
compat.sh
alias docker=podman
docker images --format '{{.Repository}}:{{.Tag}}'
sudo dnf install -y podman-docker 2>/dev/null || true
systemctl --user enable --now podman.socket
export DOCKER_HOST=unix://$XDG_RUNTIME_DIR/podman/podman.sock
curl -s --unix-socket $XDG_RUNTIME_DIR/podman/podman.sock http://d/v1.41/containers/json | head -c 120; echo
grep -n 'unqualified-search-registries' /etc/containers/registries.conf
podman rm -f web && podman image prune -f
What each line does
alias docker=podman— Quickest way to reuse scripts and habits.podman-docker— Package that installs a docker command wrapper and silences the notice.podman.socket— Optional user-level API socket compatible with the Docker REST API, for tools like Testcontainers or VS Code.DOCKER_HOST— Points docker-compatible tools at the Podman socket.registries.conf— Lists registries Podman searches for short names like nginx.
Green output
$ bash compat.sh
docker.io/library/nginx:1.27-alpine
Created symlink ~/.config/systemd/user/sockets.target.wants/podman.socket
[{"Id":"3e1a9f...","Names":["/web"],"Image":"docker.io/library/nginx:1.27-alpine","State":"running"
22:unqualified-search-registries = ["registry.fedoraproject.org", "registry.access.redhat.com", "docker.io"]
web
Expected result. Podman speaks the Docker CLI and, through an optional socket, the Docker API. The socket is started by systemd only when a client connects, so the system stays daemonless in practice.
Every command, step by step (Podman lab)
-
Install
sudo dnf install -y podmanWhat it does: Package.
Complete! -
Version
podman versionWhat it does: Check.
Version: 6.1.4 -
Info
podman infoWhat it does: Details.
ociRuntime: crun -
Machine
podman machine init && podman machine startWhat it does: macOS/Windows VM.
Machine started successfully -
Run
podman run -d --name web -p 8080:80 docker.io/library/nginx:1.27-alpineWhat it does: Start.
3e1a9f... -
List
podman psWhat it does: Running.
web Up 2 seconds -
Tree
ps -o pid,ppid,user,cmd -C conmonWhat it does: No daemon.
/usr/bin/conmon ... -
Logs
podman logs --tail 2 webWhat it does: Output.
start worker processes -
Socket
systemctl --user enable --now podman.socketWhat it does: Docker API.
Created symlink -
Clean
podman rm -f webWhat it does: Remove.
web
When it is red 1
Error: short-name "nginx" did not resolve to an alias
Cause. No search registry configured.
Fix, step by step
- Use docker.io/library/nginx or set unqualified-search-registries.
When it is red 2
Cannot connect to Podman. Please verify your connection
Cause. podman machine not started on macOS or Windows.
Fix, step by step
- podman machine start.
When it is red 3
Error: rootlessport cannot expose privileged port 80
Cause. Rootless user binding below 1024.
Fix, step by step
- Use 8080, or set net.ipv4.ip_unprivileged_port_start=80.
When it is red 4
WARN[0000] "/" is not a shared mount
Cause. Old kernel or WSL setup.
Fix, step by step
- Update; or podman machine on Windows.
When it is red 5
docker: command not found after alias
Cause. Alias not in scripts.
Fix, step by step
- Install podman-docker for a real docker command.
Practice tasks
Easy Task 1: Install
Why: Package.
What to do: Install Podman.
- dnf or apt.
Solution
podman versionEasy Task 2: Info
Why: Check.
What to do: Print runtime and storage.
- --format.
Solution
podman infoEasy Task 3: Run
Why: nginx.
What to do: Run nginx on 8080.
- Full name.
Solution
curl -s localhost:8080Medium Task 4: Tree
Why: conmon.
What to do: Show the process tree.
- ps -C.
Solution
ps -C conmonMedium Task 5: Alias
Why: Compat.
What to do: Alias docker to podman.
- Run ps.
Solution
docker psMedium Task 6: Socket
Why: API.
What to do: Enable podman.socket.
- curl the API.
Solution
systemctl --user status podman.socketHard Task 7: Machine
Why: VM.
What to do: Init a machine on macOS or Windows.
- 2 CPUs.
Solution
podman machine listHard Task 8: RHEL team
Why: Main task.
What to do: Full setup on RHEL 9.
- All checks.
Solution
podman psHomework: try at home
For a SBI-style RHEL 9 server team, install Podman, run nginx rootless on 8080, show the conmon process tree, enable the user socket and point a docker-compatible tool at it with DOCKER_HOST.
Recap
- Podman runs OCI images with docker-like commands and no daemon.
- conmon supervises each container; crun runs it.
- RHEL ships Podman by default; Ubuntu, macOS and Windows install it easily.
- podman machine provides the Linux VM on macOS and Windows.
- An optional socket gives Docker API compatibility when tools need it.
FAQ
Q. Is Podman free?
Yes, it is open source under the Apache 2.0 licence, and Podman Desktop is free too, including for companies, unlike Docker Desktop which needs a paid subscription for larger organisations.
Q. Can Podman and Docker run on the same machine?
Yes. They use separate image stores and separate containers, so docker ps and podman ps show different lists.
Q. Which version should I use?
The current Podman 6 line, 6.1.4 in October 2026; the 5.8 line still receives fixes for older distributions. Podman 5 switched rootless networking to pasta and dropped CNI for netavark, and Podman 6.1 added podman machine restart and healthchecks exported as liveness probes by podman generate kube. Distribution packages lag a little, so RHEL and Ubuntu LTS may ship an older but supported version.
Q. Where are images and containers stored?
For a rootless user under ~/.local/share/containers/storage, and for root under /var/lib/containers/storage. Configuration lives in ~/.config/containers or /etc/containers: registries.conf for registries, storage.conf for the storage driver and containers.conf for defaults such as the runtime and log driver. Deleting the user folder resets everything for that user without touching anyone else.
Q. Do Dockerfiles work with Podman?
Yes. podman build uses Buildah inside and reads the same Dockerfile, also called Containerfile. Multi-stage builds, build arguments, cache mounts and --platform all work, and the images can be pushed to Docker Hub, Amazon ECR or Quay without change.
Q. Does Podman work in Jenkins or GitLab CI?
Yes, and rootless Podman inside a CI container avoids mounting the Docker socket, which is a common privilege escalation path.
Interview questions
-
What is the main architectural difference between Docker and Podman?
Model answer: Docker uses a client-server model with a root daemon; Podman forks containers directly under conmon with no daemon.
-
Why is daemonless more secure?
Model answer: No root socket to abuse, containers owned by the user, no single process controlling all containers.
-
What are conmon and crun?
Model answer: conmon monitors a container; crun is the OCI runtime that creates it.
-
What happens to running containers when you upgrade Podman?
Model answer: Nothing. Each container is supervised by its own conmon process, so upgrading the podman package does not stop it, unlike restarting dockerd, which stops every container unless live-restore is enabled.
-
How do you run a Podman container at boot?
Model answer: Write a Quadlet .container file under ~/.config/containers/systemd and let systemd start it, with loginctl enable-linger for rootless users.
-
How does Podman work on a Mac?
Model answer: podman machine runs a Linux VM, and the CLI talks to it over SSH.
Quick quiz
Q1. What is missing in Podman compared to Docker?
A daemon.
Q2. Which process supervises a container?
conmon.
Q3. Which runtime is default?
crun.
Q4. What does podman machine create?
A Linux VM.
Q5. How do Docker tools reach Podman?
podman.socket and DOCKER_HOST.
Samajla ka?