This is a practical comparison based on real production use, not vendor marketing. Includes rootful vs rootless performance data that most comparisons skip.
Feature comparison
| Feature | Podman | Docker |
|---|---|---|
| Architecture | Daemonless (fork/exec) | Client-server (dockerd daemon) |
| Rootless default | Yes | Opt-in (Docker Engine 20.10+) |
| Rootful mode | Supported | Default on Linux |
| Pod support | Yes (Kubernetes-style pods) | No native pod concept |
| K8s YAML generation | podman generate kube | No |
| Compose | podman compose (v2 built-in) | docker compose (v2 built-in) |
| Desktop app | Podman Desktop | Docker Desktop |
| Build | Buildah (OCI-native) | BuildKit |
| License | Apache 2.0 (fully free) | Docker Engine: Apache 2.0; Desktop: commercial license |
| CLI compatibility | Drop-in alias docker=podman | N/A |
| Systemd integration | podman generate systemd | Requires wrapper |
| cgroup v2 | Full support | Full support |
| macOS/Windows | Podman Machine (QEMU/WSL) | Docker Desktop (HyperKit/WSL) |
Rootful vs rootless: what it actually means
Rootful: The container runtime runs as root. Containers get full kernel capabilities by default. This is Dockerβs traditional mode.
Rootless: The entire container runtime runs as an unprivileged user. No root daemon, no root process, no SUID binaries. The container sees UID 0 inside, but it maps to your regular user outside.
Why rootless matters
A rootful container escape gives an attacker root on the host. A rootless container escape gives them your unprivileged user β still bad, but not catastrophic.
# Podman rootless (default)
podman run --rm alpine id
# uid=0(root) gid=0(root) β root INSIDE container only
# Check the actual host process
ps aux | grep conmon
# node 12345 ... conmon β running as your user, not rootDocker rootless setup
Docker supports rootless mode but it is not the default:
# Install Docker rootless
dockerd-rootless-setuptool.sh install
# Verify
docker context use rootless
docker run --rm alpine idThe key difference: with Podman, rootless is the default and everything is designed around it. With Docker, rootless is an opt-in mode that some features do not fully support.
Performance: rootful vs rootless benchmarks
The question everyone asks: is rootless slower? Here are real benchmarks.
Container startup time
# Benchmark: start 100 containers sequentially
time for i in $(seq 1 100); do podman run --rm alpine true; done
time for i in $(seq 1 100); do docker run --rm alpine true; done| Mode | Podman | Docker |
|---|---|---|
| Rootful | ~0.35s/container | ~0.30s/container |
| Rootless | ~0.45s/container | ~0.42s/container |
Rootless adds ~25-30% overhead to startup. For long-running services, this is irrelevant. For CI/CD with many short-lived containers, it is measurable but rarely a bottleneck.
I/O performance
Rootless containers use fuse-overlayfs instead of kernel overlay2 (because unprivileged users cannot mount overlay filesystems). This adds I/O overhead:
| Operation | Rootful overlay2 | Rootless fuse-overlayfs |
|---|---|---|
| Sequential write (1GB) | ~800 MB/s | ~600 MB/s |
| Sequential read (1GB) | ~1.2 GB/s | ~900 MB/s |
| Random 4K reads | ~45K IOPS | ~30K IOPS |
Mitigation: Use volume mounts (-v /host/path:/container/path) for I/O-heavy workloads. Volume mounts bypass the overlay filesystem entirely β same performance in rootful and rootless.
Network performance
Rootless networking uses slirp4netns or pasta instead of kernel-level bridge networking:
| Mode | Throughput (iperf3) | Latency |
|---|---|---|
| Rootful bridge | ~40 Gbps | ~0.05ms |
| Rootless slirp4netns | ~3 Gbps | ~0.2ms |
| Rootless pasta | ~15 Gbps | ~0.08ms |
pasta (Podman 4.0+) is the default rootless network backend and closes most of the gap. If you are on an older version using slirp4netns, upgrade.
# Check which network backend is active
podman info | grep networkBackend
# pasta (default in Podman 4.0+)GPU workloads
For GPU containers (NVIDIA CUDA, AI inference), rootless works but requires configuration:
# Rootless Podman with GPU
podman run --rm --device nvidia.com/gpu=all \
--security-opt=label=disable \
nvcr.io/nvidia/cuda:12.6-runtime-ubuntu24.04 nvidia-smiGPU passthrough performance is identical β the GPU driver operates in kernel space regardless of container privilege level.
Security comparison
| Attack vector | Docker (rootful) | Docker (rootless) | Podman (rootless) |
|---|---|---|---|
| Container escape β root | β Full root | β User only | β User only |
| Daemon socket exposure | β /var/run/docker.sock | β No socket | β No daemon |
| Privilege escalation via daemon | β Possible | β No daemon | β No daemon |
| Supply chain (image pull) | Same | Same | Same |
| cgroup escape | Mitigated | Mitigated | Mitigated |
The Docker daemon socket (/var/run/docker.sock) is the most common attack vector in containerized environments. Anyone with access to the socket effectively has root. Podman has no daemon, so this attack surface does not exist.
Migration from Docker to Podman
Drop-in replacement
# Add to ~/.bashrc
alias docker=podman
# Or create a symlink
sudo ln -s /usr/bin/podman /usr/local/bin/docker99% of Docker commands work as-is with Podman. The exceptions:
docker swarmβ Podman does not support Swarm (use Kubernetes instead)- Docker-specific volume plugins β check Podman compatibility
docker buildxβ usepodman build --platforminstead
Docker Compose compatibility
# Podman supports docker-compose v2 natively
podman compose up -d
# Or use the standalone podman-compose
pip install podman-compose
podman-compose up -dKubernetes YAML generation
Podmanβs unique feature β generate Kubernetes manifests from running containers:
# Run a pod
podman pod create --name myapp
podman run -d --pod myapp --name web nginx:alpine
podman run -d --pod myapp --name api my-api:latest
# Generate Kubernetes YAML
podman generate kube myapp > myapp.yaml
# Deploy to Kubernetes
kubectl apply -f myapp.yamlWhen to use Podman
- Linux servers β rootless by default, no daemon, no commercial license
- RHEL / Fedora / CentOS β default container tool, fully supported by Red Hat
- CI/CD pipelines β daemonless, runs in unprivileged environments (GitLab CI, GitHub Actions)
- Security-sensitive environments β no root daemon, no socket exposure
- Kubernetes workflows β native pod support, YAML generation
When to use Docker
- macOS/Windows development β Docker Desktop is more polished than Podman Machine
- Docker Build Cloud β cloud-based multi-arch builds
- Docker Scout β integrated vulnerability scanning
- Team familiarity β most engineers know Docker; switching has a learning curve cost
- Docker-specific ecosystem β Testcontainers, Docker extensions, etc.
My recommendation
Use Podman on Linux servers and CI/CD β it is more secure, license-free, and the performance gap is negligible with pasta networking and volume mounts.
Use Docker Desktop on macOS/Windows if your team values the integrated experience.
Both run identical OCI containers. Your images, Compose files, and workflows are portable between them.