Something weird happened in the container world. Docker — the tool that literally defined containerization — is quietly losing ground. Not to some flashy startup, but to an open-source project most developers still haven't tried: Podman.
The numbers tell an interesting story. Podman crossed 30,000 GitHub stars in early 2026. Red Hat, SUSE, and Canonical are shipping it by default. Kubernetes dropped Docker as a runtime way back in 1.24. And Docker Desktop's licensing changes turned "free tool everyone uses" into "thing that costs $24/month/seat for companies over 250 employees."
But here's what most comparison articles get wrong: this isn't about whether Podman is "better" than Docker. It's about whether your specific workflow, security requirements, and deployment targets make one a materially better fit. Let's dig into the data, the migration path, the gotchas — and yes, that "50% performance boost" claim.
The single biggest difference between Docker and Podman comes down to one question: daemon or no daemon?
Docker uses a client-server architecture. When you type docker run nginx, the CLI sends a REST API call to dockerd — a privileged, root-owned daemon listening on /var/run/docker.sock. That daemon talks to containerd, which talks to runc, which finally creates your container. Everything flows through that single, always-running, root-privileged process.
Podman takes a fundamentally different path. There is no daemon. When you run podman run nginx, the binary directly forks the container using conmon (a lightweight container monitor) and then exits. Each container is an independent child process — no central service, no privileged socket, no always-on overhead.
The implications are huge:
conmon, not the CLI/var/run/docker.sock for an attacker to abuse. The infamous "if you can write to the Docker socket, you can root the host" attack class simply doesn't existpodman generate systemd or the newer Quadlet mechanismThe idle memory difference alone is staggering: Podman consumes 0 MB when no containers are running. Docker's daemon idles at 140–180 MB regardless of workload. On a 100-node fleet, that's ~14 GB of freed RAM before a single workload starts.
Security is the strongest argument for Podman in 2026. Let's cut through the jargon.
Docker's daemon runs as root by default. When you mount a volume like -v /host/path:/container/path, the container process accesses those files as root on your host. Docker has mitigations — user namespaces, seccomp profiles, AppArmor — but they're opt-in and often misconfigured.
Compromising the Docker daemon means full root access to the host. Recent CVEs prove this isn't theoretical: CVE-2025-9074 (CVSS 9.3) allowed malicious containers to bypass authentication and access the Docker Engine without the socket even being mounted.
Podman runs rootless by default. No setup required:
# This just works — no root, no daemon, no configuration
podman run -d nginx
# Verify: it's running as YOUR user, not root
podman top -l user
Under the hood, Podman uses Linux user namespaces to map container UIDs to unprivileged host UIDs. Inside the container, nginx might think it's running as root (UID 0), but on your host it's actually your UID. Even if a container escape occurs, the attacker gets your unprivileged user access, not root.
| Security Feature | Docker (default) | Podman |
|---|---|---|
| Daemon privilege | Root | No daemon |
| Container UID on host | Root | Mapped to user |
| Privileged socket | /var/run/docker.sock (root) |
None |
| SELinux/AppArmor | Optional | Default |
| CVE impact of daemon compromise | Full root | N/A (no daemon) |
NIST guidelines now recommend rootless containers. Compliance frameworks increasingly favor daemon-free architectures. If your team handles SOC 2, PCI-DSS, or HIPAA workloads, Podman's security posture is materially easier to audit and defend.
Let's separate the real benchmarks from the marketing. Multiple independent tests in 2025–2026 paint a nuanced picture:
| Benchmark | Podman 5.x | Docker 27.x | Winner |
|---|---|---|---|
| Cold start (alpine) | 0.81s | 1.23s | Podman ~34% faster |
| Cold start (node:20) | 1.07s | 1.61s | Podman ~34% faster |
| Cold start (postgres:16) | 1.94s | 2.42s | Podman ~20% faster |
| Build time (multi-stage Go) | 52s | 47s | Docker ~10% faster (BuildKit) |
| Memory, 30 containers | 2.55 GB | 3.0 GB + 140 MB daemon | Podman ~17% less |
| HTTP throughput (nginx) | ~78,400 RPS | ~78,100 RPS | Essentially equal |
| Container API latency | 32ms | 21ms | Docker ~34% faster |
The results split cleanly along architectural lines. Anything that benefits from "no daemon" — startup time, memory footprint, idle behavior — favors Podman. Anything that benefits from a long-lived service with warm caches — API latency, multi-stage builds with cached layers, rootful bridge networking — favors Docker.
The HTTP throughput tie is the most important number for production: once a container is up, both deliver identical request-handling performance. The runtime gets out of the way.
So where does "50% faster" come from? It's typically measured in cold-start scenarios on memory-constrained CI runners, where Docker's daemon overhead compounds. At scale (100+ containers), Podman's process-per-container model also scales more linearly than Docker's single-daemon bottleneck.
The real win isn't raw speed — it's resource efficiency. Podman's 65% lower idle memory and zero-daemon model mean more of your hardware budget goes to actual workloads.
The good news: Podman's CLI is intentionally ~95% compatible with Docker's. For most users, migration is shockingly smooth.
# Ubuntu/Debian
sudo apt-get install -y podman
# RHEL/Fedora/CentOS
sudo dnf install -y podman
# macOS
brew install podman
podman machine init
podman machine start
# Add to ~/.bashrc or ~/.zshrc
alias docker=podman
# These now all work identically:
docker pull nginx
docker run -d -p 8080:80 nginx
docker ps
docker build -t myapp .
docker exec -it myapp bash
Nearly every basic Docker command maps 1:1 to Podman. docker run, docker build, docker ps, docker logs, docker images — they all work.
OCI images are the same format. You can pull directly or export/import:
# Option 1: Pull directly from Docker Hub
podman pull docker.io/library/nginx:latest
# Option 2: Export from Docker, import to Podman
docker save myimage:latest > myimage.tar
podman load < myimage.tar
# Migrate volumes
docker volume inspect my-data --format '{{.Mountpoint}}'
sudo tar czf volume-backup.tar.gz -C /var/lib/docker/volumes/my-data/_data .
podman volume create my-data
sudo tar xzf volume-backup.tar.gz -C $(podman volume inspect my-data --format '{{.Mountpoint}}')
You have two solid options:
# Option A: podman-compose (Python, simpler)
pip3 install podman-compose
podman-compose up -d
# Option B: Docker Compose v2 via Podman's compatibility socket (full feature parity)
systemctl --user enable --now podman.socket
export DOCKER_HOST=unix://$XDG_RUNTIME_DIR/podman/podman.sock
docker compose up -d
Option B is the recommended path — it gives you full Docker Compose v2 compatibility because Docker Compose talks to Podman's Docker-compatible API.
You don't need to rip out Docker on day one. Run both:
# Docker project
docker compose -f docker-project/compose.yaml up -d
# Podman project (new)
podman run -d --name new-project -p 3000:3000 node:20-alpine
This is the #1 pain point when migrating from Docker to Podman. If you've ever seen Permission denied on a bind-mounted folder in Podman, here's exactly what's happening and how to fix it.
Rootless Podman uses user namespaces to remap UIDs. Your host UID (e.g., 1000) appears as root (UID 0) inside the container's namespace. When you mount a host directory with -v /home/you/data:/app/data, the container sees that directory as owned by root:root — not your container's unprivileged user.
# Inside the container, the mounted folder looks like this:
$ ls -al /app
drwxr-xr-x 2 root root 4096 Jun 23 10:00 data
# Your container user (e.g., UID 200) can't write here!
Use podman unshare to operate inside Podman's user namespace:
# Step 1: Find the container's UID (from Dockerfile's USER line, or your -u flag)
# Example: Nexus runs as UID 200
# Step 2: Fix permissions from within the Podman namespace
podman unshare chown 200:200 -R /home/you/data
:z and :Z FlagsIf SELinux is enforcing (check with getenforce), you need to label your volumes:
# :z — shared label (multiple containers can access)
podman run -v /host/path:/container/path:z myimage
# :Z — private label (only THIS container can access — safer)
podman run -v /host/path:/container/path:Z myimage
⚠️ Critical: Using :Z on a home directory will relabel it and potentially break your desktop! Only use it on dedicated data directories.
| Scenario | Solution |
|---|---|
| Container can't write to mounted folder | podman unshare chown UID:GID -R /host/path |
| SELinux denial on RHEL/Fedora | Add :z or :Z to volume mount |
| Container needs host networking | Use --network host (requires root) or map specific ports |
| Port < 1024 in rootless mode | Map to port >= 1024 (8080:80) or set net.ipv4.ip_unprivileged_port_start=80 |
| Multiple containers need shared volume | Use :z (shared SELinux label) |
--userns=keep-id ShortcutPodman 5.x added a game-changing flag:
# Maps your host UID directly into the container — no more permission puzzles
podman run --userns=keep-id -v $(pwd)/data:/app/data:Z myimage
This keeps your host UID inside the container, so files you create in the mounted volume are owned by you on the host. For development workflows, this is the closest thing to Docker's behavior.
| Operation | Docker | Podman |
|---|---|---|
| Run a container | docker run -d nginx |
podman run -d nginx |
| List containers | docker ps |
podman ps |
| Build an image | docker build -t app . |
podman build -t app . |
| Exec into container | docker exec -it app bash |
podman exec -it app bash |
| View logs | docker logs app |
podman logs app |
| Pull image | docker pull nginx |
podman pull nginx |
| Compose up | docker compose up -d |
podman-compose up -d |
| Create a pod | ❌ Not available | podman pod create --name web |
| Generate K8s YAML | ❌ Not available | podman generate kube web |
| Generate systemd unit | ❌ Not available | podman generate systemd web |
| Run K8s YAML locally | ❌ Not available | podman play kube web.yaml |
Podman brings several features Docker simply doesn't have:
1. Native Pods: Group containers that share network namespaces — just like Kubernetes pods:
podman pod create --name webapp -p 8080:80
podman run -d --pod webapp --name frontend nginx
podman run -d --pod webapp --name api node:20-slim
# frontend can reach api at localhost:3000
2. Generate Kubernetes YAML from running containers:
podman generate kube webapp > k8s-deployment.yaml
3. Run Kubernetes YAML locally without a cluster:
podman play kube deployment.yaml
podman play kube deployment.yaml --down
4. Native systemd integration:
podman generate systemd --new mycontainer > ~/.config/systemd/user/mycontainer.service
systemctl --user enable --now mycontainer
Podman isn't perfect for every scenario. Here's where Docker still wins:
The most pragmatic approach in 2026 is often both:
Migration doesn't mean burning the Docker bridge. Start with one project. Test the alias docker=podman trick. Run Podman for new services while keeping Docker for existing ones. The OCI standard ensures they play nicely together.
Is Podman worth migrating to? For Linux production workloads — absolutely. The security posture (rootless-by-default, no privileged daemon socket) alone justifies the effort. The 65% lower idle memory and 33% faster container startup are genuine bonuses, not marketing fluff.
The migration itself is surprisingly low-friction. With near-identical CLI syntax, OCI image compatibility, and the DOCKER_HOST socket trick for Docker Compose v2 support, you can be running Podman in production within an afternoon.
That "50% performance boost" headline you've seen floating around? It's cherry-picked from specific CI/CD cold-start benchmarks — but the underlying data is real. Podman is lighter, faster to start, and far more secure by default. In 2026, Docker's daemon has become a liability that Podman neatly eliminates.