Press ESC to close Press / to search

Podman 5 Complete Guide: Replace Docker with Rootless Containers on Linux

πŸ“‘ Table of Contents

Docker has been synonymous with containers for most of a decade. It’s the tool most developers reach for first, the one that appears in every tutorial, and the one that ships as part of countless CI/CD setups. But in enterprise Linux environments β€” particularly anywhere running RHEL, Rocky Linux, or Fedora β€” Podman has been quietly displacing Docker, and with Podman 5, the gap in features has essentially closed while Podman’s architectural advantages have only grown. If you haven’t made the switch, or if you’ve been putting it off, this is the guide that walks you through everything you need to know.

What Podman Is (And What Makes It Different)

Podman is a daemonless, OCI-compliant container engine. “Daemonless” is the key word. Docker runs a privileged background daemon (dockerd) that all container operations go through. That daemon runs as root. Every docker command is talking to that daemon. If the daemon crashes, all your containers go with it. If you want to run containers as a non-root user, Docker requires group membership that effectively grants root-equivalent access to the host.

Podman takes a different approach. There is no daemon. When you run podman run nginx, Podman directly forks a container process using the container runtime (runc or crun). The command exits when the container starts. No persistent privileged process is sitting between you and your containers. Each container is a direct child process of the user who launched it.

This architecture has several concrete benefits:

  • No single point of failure for all running containers
  • Rootless containers work properly without privilege escalation
  • Containers can integrate cleanly with systemd as standard user services
  • Attack surface is dramatically reduced β€” no always-on root daemon
  • Audit trails show the actual user who ran the container, not just “docker daemon”

Podman is developed by Red Hat and is the default container engine on RHEL, Rocky Linux, AlmaLinux, and Fedora. It implements the same CLI interface as Docker, so migration is mostly a matter of replacing the word “docker” with “podman” in your scripts.

Rootless vs. Rootful Containers

Understanding the rootless/rootful distinction is essential before you start deploying with Podman.

A rootful container runs as root on the host. The container process is isolated, but the container runtime itself has root privileges on the host filesystem. If something escapes the container β€” via a kernel exploit or misconfiguration β€” it has root access to the host. Docker containers are rootful by default.

A rootless container runs under a normal user account. The container sees itself as root (UID 0) inside the container namespace, but that UID maps to an unprivileged user on the host through user namespace remapping. On the host, the container process has no more privileges than the user who launched it.

Podman defaults to rootless. When you run a container as a normal user, Podman uses subordinate UID/GID mappings defined in /etc/subuid and /etc/subgid to create the namespace mapping. These entries are created automatically when you add a user with useradd on modern distributions.

Verify your subordinate UID mappings:

grep $(whoami) /etc/subuid /etc/subgid

You should see output like:

/etc/subuid:youruser:100000:65536
/etc/subgid:youruser:100000:65536

This means your user can map up to 65,536 UIDs starting at 100000 on the host, which covers the full range of UIDs a container might use internally.

Installing Podman 5

RHEL 9 / Rocky Linux 9 / AlmaLinux 9

Podman 5 ships in the default AppStream repository on RHEL 9.x and its downstream equivalents:

dnf install -y podman podman-compose
podman --version

For the very latest Podman 5.x builds:

dnf install -y epel-release
dnf upgrade -y podman

Ubuntu 24.04 LTS

apt update
apt install -y podman podman-compose
podman --version

For Podman 5 specifically on older Ubuntu releases, use the Kubic repository:

. /etc/os-release
echo "deb https://download.opensuse.org/repositories/devel:/kubic:/libcontainers:/unstable/xUbuntu_${VERSION_ID}/ /" \
  | tee /etc/apt/sources.list.d/devel:kubic:libcontainers:unstable.list
curl -fsSL "https://download.opensuse.org/repositories/devel:/kubic:/libcontainers:/unstable/xUbuntu_${VERSION_ID}/Release.key" \
  | gpg --dearmor | tee /etc/apt/trusted.gpg.d/devel_kubic_libcontainers_unstable.gpg > /dev/null
apt update
apt install -y podman

Fedora

dnf install -y podman podman-compose

Fedora typically ships very recent Podman builds in its default repository.

Core Podman Commands

If you know Docker, you already know most of Podman’s command syntax. Here are the everyday operations:

Pull an image:

podman pull docker.io/library/nginx:latest

Run a container in the foreground:

podman run --rm -it ubuntu:24.04 bash

Run a detached container with port mapping:

podman run -d --name webserver -p 8080:80 nginx:latest

List running containers:

podman ps
podman ps -a   # include stopped containers

View container logs:

podman logs -f webserver

Execute a command in a running container:

podman exec -it webserver bash

Stop and remove:

podman stop webserver
podman rm webserver

Build an image from a Containerfile (or Dockerfile β€” Podman reads both):

podman build -t myapp:latest -f Containerfile .

Pod Management

One feature that sets Podman apart from Docker CLI is native pod support. Pods are groups of containers that share a network namespace β€” the same concept as Kubernetes pods. This makes Podman’s local development model much closer to what runs in production Kubernetes.

Create a pod:

podman pod create --name myapp-pod -p 8080:80

Add containers to the pod:

podman run -d --pod myapp-pod --name app nginx:latest
podman run -d --pod myapp-pod --name sidecar busybox sleep 3600

List pods:

podman pod list
podman pod ps

Stop and remove an entire pod:

podman pod stop myapp-pod
podman pod rm myapp-pod

This is genuinely useful when you want to test a multi-container application locally with networking that mirrors how it will behave in Kubernetes.

Podman Compose

Podman Compose is a Python tool that implements the Docker Compose file spec on top of Podman. If you have an existing docker-compose.yml, you can run it with Podman Compose without modification in most cases.

Install it:

pip3 install podman-compose
# or
dnf install podman-compose   # RHEL/Fedora
apt install podman-compose   # Ubuntu

Use it like Docker Compose:

podman-compose up -d
podman-compose logs -f
podman-compose down

An example compose.yml for a WordPress stack:

services:
  db:
    image: docker.io/library/mariadb:10.11
    environment:
      MARIADB_ROOT_PASSWORD: rootpass
      MARIADB_DATABASE: wordpress
      MARIADB_USER: wpuser
      MARIADB_PASSWORD: wppass
    volumes:
      - db_data:/var/lib/mysql

  wordpress:
    image: docker.io/library/wordpress:latest
    ports:
      - "8080:80"
    environment:
      WORDPRESS_DB_HOST: db
      WORDPRESS_DB_NAME: wordpress
      WORDPRESS_DB_USER: wpuser
      WORDPRESS_DB_PASSWORD: wppass
    depends_on:
      - db

volumes:
  db_data:

Run it rootless:

podman-compose up -d

Quadlets: Systemd Integration the Right Way

Quadlets are one of the most significant features introduced in Podman 4.4 and refined in Podman 5. They let you define containers as systemd unit files using a clean, declarative syntax β€” without writing a shell script or using podman generate systemd.

Create a directory for your user-level Quadlet files:

mkdir -p ~/.config/containers/systemd/

Create a file called ~/.config/containers/systemd/nginx.container:

[Unit]
Description=Nginx Web Server
After=network-online.target

[Container]
Image=docker.io/library/nginx:latest
PublishPort=8080:80
Volume=/home/youruser/html:/usr/share/nginx/html:ro
Environment=NGINX_HOST=example.com

[Service]
Restart=always

[Install]
WantedBy=default.target

Reload systemd and start the service:

systemctl --user daemon-reload
systemctl --user enable --now nginx.service
systemctl --user status nginx.service

Systemd now manages the container lifecycle β€” start on login, restart on failure, log via journald. No cron job, no wrapper script. For system-level (rootful) containers, put the file in /etc/containers/systemd/ and use systemctl without the --user flag.

Quadlets also support pods and volumes as first-class unit types (.pod and .volume files), letting you define entire multi-container stacks as systemd units.

Migrating from Docker

The practical migration path is straightforward:

  1. Install Podman alongside Docker (they can coexist)
  2. Set up the Docker compatibility alias if needed: alias docker=podman
  3. Test your existing Docker commands β€” most work without changes
  4. Note that Podman uses a separate image store, so you’ll need to pull images again
  5. Migrate your docker-compose files to podman-compose or Quadlets
  6. Update any references to the Docker socket (/var/run/docker.sock) β€” Podman’s socket is at /run/user/$UID/podman/podman.sock
  7. Stop and disable the Docker daemon: systemctl disable --now docker

For CI/CD pipelines that build Docker images, Podman can build and push OCI-compatible images to any registry using the same Dockerfile/Containerfile syntax:

podman build -t registry.example.com/myapp:1.0 .
podman push registry.example.com/myapp:1.0

Tools like Buildah (which Podman uses under the hood) give you even more fine-grained control over image construction if you need it.

Podman 5 Specific Improvements

Podman 5 introduced several notable changes worth knowing about:

  • Netavark and Aardvark-DNS are now the default network stack, replacing CNI plugins. This gives more consistent networking behavior and better performance for rootless containers.
  • Improved containers.conf handling with better defaults and cleaner override layering.
  • Pasta networking mode as an alternative to slirp4netns for rootless containers β€” faster and with better IPv6 support.
  • Enhanced Quadlet support with full pod and volume unit types.
  • Podman Machine improvements for macOS and Windows users who need a Linux VM to run Linux containers.

Key Takeaways

  • Podman is daemonless β€” no privileged background process, no single point of failure
  • Rootless containers run under your actual user account; container root maps to your UID on the host
  • The CLI is Docker-compatible, making migration a mostly search-and-replace exercise
  • Pods let you group containers sharing a network namespace, matching the Kubernetes model locally
  • Podman Compose handles docker-compose.yml files without modification in most cases
  • Quadlets replace podman generate systemd with a clean declarative unit file format
  • Podman 5’s new Netavark/Aardvark-DNS stack and Pasta networking mode improve rootless networking significantly
  • On RHEL and its derivatives, Podman is the supported, default container engine β€” Docker is not

Was this article helpful?

Advertisement
R

About Ramesh Sundararamaiah

Red Hat Certified Architect

Expert in Linux system administration, DevOps automation, and cloud infrastructure. Specializing in Red Hat Enterprise Linux, CentOS, Ubuntu, Docker, Ansible, and enterprise IT solutions.

🐧 Stay Updated with Linux Tips

Get the latest tutorials, news, and guides delivered to your inbox weekly.

Advertisement

Add Comment


↑