Back to Blog
Containers22 January 2025Michael Hettwer10 min read

Podman as a Docker Alternative: A Production-Ready Guide

Podman's rootless, daemonless architecture makes it a compelling alternative to Docker for security-conscious teams. Here's how to migrate smoothly.

Docker changed how we deploy software. Podman wants to change how we run containers — without a privileged daemon sitting between your application and the kernel. If you are running containers in production on Linux, understanding Podman is worth your time.

Why Podman?

  • No daemon: each container is a direct child process of the user who started it
  • Rootless by default: containers run without root privileges on the host
  • OCI-compliant: runs the same images as Docker (from Docker Hub, GHCR, or any OCI registry)
  • Drop-in Docker CLI compatibility: alias docker=podman and most scripts just work
  • Native systemd integration: generate unit files directly from container definitions
  • Pod support: group containers like Kubernetes pods, without Kubernetes overhead

Installation

bash
# Debian / Ubuntu
apt update && apt install -y podman

# RHEL / AlmaLinux / Rocky (already included in most RHEL 8+ images)
dnf install -y podman

# Verify
podman version
podman info

Running Your First Rootless Container

bash
# Pull and run nginx (rootless — no sudo needed)
podman run -d --name web -p 8080:80 docker.io/library/nginx:alpine

# List running containers
podman ps

# Check logs
podman logs web

# Stop and remove
podman stop web && podman rm web
Note:

Rootless containers cannot bind to ports below 1024 without kernel capability adjustments. Use ports >= 1024 in development, and an nginx/HAProxy reverse proxy in production.

Migrating from Docker Compose

Podman supports Docker Compose files via podman-compose (community project) or the built-in Quadlet system. For production, Quadlet — which generates systemd unit files from container definitions — is the more reliable path.

bash
# Install podman-compose for development parity
pip3 install podman-compose

# Run an existing docker-compose.yml
podman-compose up -d

# For production: generate systemd units via Quadlet
# Place a .container file in ~/.config/containers/systemd/
mkdir -p ~/.config/containers/systemd
ini
# ~/.config/containers/systemd/web.container
[Unit]
Description=Nginx web server
After=network-online.target

[Container]
Image=docker.io/library/nginx:alpine
PublishPort=8080:80
Volume=/srv/www:/usr/share/nginx/html:ro,Z

[Service]
Restart=always

[Install]
WantedBy=default.target
bash
# Reload systemd and start the container unit
systemctl --user daemon-reload
systemctl --user enable --now web.service
systemctl --user status web.service

Security Considerations

  • Use :Z or :z volume mount labels so SELinux allows container access to host paths
  • Run with --security-opt no-new-privileges to prevent privilege escalation inside containers
  • Set --read-only on the container filesystem where possible
  • Specify --user inside the container to avoid running as UID 0 even within the container namespace
  • Regularly audit images with podman image scan or trivy

Podman is production-ready and widely deployed, including in Red Hat OpenShift under the hood. The migration from Docker is typically a half-day effort for most stacks. The security and operational benefits — no privileged daemon, native systemd integration, rootless operation — make it worth the investment.