Docker vs. Podman
Your docker run nginx -p 80:80 command works perfectly. Until the Docker daemon crashes, you lose all container management capabilities. That’s it — a single point of failure.
Containerization powers modern development, but the landscape has moved beyond Docker’s early dominance. Teams now want better security, simpler architecture, and native Kubernetes alignment. Podman has emerged as a serious alternative.
This post breaks down the architectural differences between Docker and Podman, why one is ideal for laptops while the other wins on Linux servers, and how each runtime works under the hood.
How Docker Works: The Daemon-First Architecture
The core difference between Docker and Podman is their architecture. When you run a docker run command, your command doesn’t create the container directly. Instead, it sends a request to the Docker daemon — a background process that runs continuously on your machine and listens for commands.
sequenceDiagram
participant CLI as $ docker run nginx
participant Client as Docker CLI
participant Daemon as Docker Daemon (socket)
participant Tools as runc, libcontainer
participant Kernel as Linux Kernel
CLI->>Client: "docker run nginx"
Client->>Daemon: POST /containers/create (via socket)
Daemon->>Tools: "allocate resources"
Tools-->>Daemon: "resources ready"
Daemon->>Kernel: namespace + cgroup setup
Kernel-->>Tools: "containers created"
Tools-->>Daemon: "container started"
Daemon-->>Client: 201 Created
The daemon then uses supporting tools (runc, libcontainer) to make system calls to the Linux kernel. These tools create isolated namespaces for network and file systems, so containers get their own view of resources.
The catch: the daemon must be alive for everything to work. If it crashes, container management stops dead.
# Docker daemon is down — you are locked out of container management
$ docker ps
❌ docker: error while connecting to the daemon: connect: refuse connection
# You cannot even stop running containers
$ docker stop my-app
❌ docker: error while connecting to the daemon...
How Podman Works: The Daemonless Alternative
Podman eliminates the daemon entirely. When you run a podman run command, Podman works directly with lower-level tools like crun (Container Runtime Universally). These tools interact straight with the Linux kernel to provision container resources.
graph TD
subgraph Docker Architecture
A[User] --> B[Docker CLI]
B --> C{Daemon}
C --> D[runc / libcontainer]
D --> E[Linux Kernel]
end
subgraph Podman Architecture
F[User] --> G[Podman CLI]
G --> H[crun / runc]
H --> I[Linux Kernel]
end
style B fill:#e1f5fe,stroke:#01579b
style F fill:#e1f5fe,stroke:#01579b
No central daemon managing everything. Each container command operates independently, which improves resilience but requires understanding how the tools interact directly with kernel primitives like namespaces and cgroups.
| Aspect | Docker | Podman |
|---|---|---|
| Architecture | Daemon-first (client-server) | Daemonless (direct kernel calls) |
| Process Model | CLI → Daemon → Tools → Kernel | CLI → Tools → Kernel |
| Root Requirement | Requires root privileges for daemon | Runs as regular user |
| Single Point of Failure | Yes (daemon) | No |
| Socket Usage | Unix socket /var/run/docker.sock | No daemon socket required |
The architecture change means each container command works independently.
flowchart LR
A[Docker Run] --> B{Daemon Alive?}
B--No--> C[All operations fail]
B--Yes--> D[runc allocates resources]
D --> E[Container starts]
F[Podman Run] --> G[crun allocates resources]
G --> H[Container starts]
Security: Root vs Non-Root Execution
The security implications of these architectural choices are substantial. The Docker daemon traditionally runs with root privileges. Anyone who can control the daemon has potentially powerful access to the host system.
graph TD
subgraph Docker Privilege Chain
A[Container Compromised] --> B{Root Access to Host?}
B--Yes--> C[Full Host Compromise]
end
subgraph Podman Privilege Chain
D[Container Compromised] --> E[Regular User Access]
E --> F[Limited Host Impact]
end
style C fill:#ff6b6b,stroke:#ff4757
style F fill:#2ecc71,stroke:#27ae60
Here’s what that actually looks like in practice:
# Inside a compromised Docker container with root privileges
$ whoami
root
# This container can access the host filesystem
$ mount -t bind / /host/mount/
# This container can potentially escape to the host
$ nsenter --mount -t $$ -p /proc/1/ns/mnt
Now try the same in Podman:
# Inside a compromised Podman container running as regular user
$ whoami
eduardo
# Attempting to mount host filesystem fails
$ mount -t bind / /host/mount/
❌ permission denied
Podman was explicitly designed to work as a non-root user. If a container is compromised, the damage is limited to whatever privileges the regular user has.
Kubernetes Alignment and Practical Migration
Podman borrows the concept of pods from Kubernetes. You can group related containers so they share resources, making Podman a natural stepping stone if you’re heading toward Kubernetes.
classDiagram
class Container {
-int id
+String name
+String image
+List resources
+start()
+stop()
}
class Pod {
-List containerRefs
-String name
+SharedNetworkSpace network
+SharedVolumeStore volumes
Container* containers
+startAll()
+stopAll()
}
Pod "1" *-- "*" Container
The migration path is smooth. docker run becomes podman run. docker build becomes podman build. Many users just create an alias so typing “Docker” actually runs Podman, preserving their muscle memory:
# Simple alias for seamless migration
echo 'alias docker=podman' >> ~/.zshrc
# Now your existing scripts work unchanged
$ docker run nginx:alpine
# Actually runs podman under the hood
Migration checklist:
- Install Podman on your system
- Test basic operations:
podman run,podman ps,podman stop - Create a Docker alias if you want to preserve your typing habits
- Configure project-specific settings: registry, volumes, network
# Verify Podman is installed and running
$ podman --version
podman version 4.6, build abc123, 2026-08-09
# Test a basic container run
$ podman run -d --name test-nginx nginx:alpine
# Container started successfully
$ podman ps
NAME IMAGE STATUS UP PORTS
test-nginx nginx:alpine Up 0s 80/tcp
$ podman stop test-nginx
# Container stopped successfully
So Which One Should You Use?
It’s not about which is objectively better. It’s about what fits your workflow.
- Docker — the daemon-based architecture provides a polished, familiar experience that is ideal for development on laptops and Macs.
- Podman — the daemonless design offers better security, simpler architecture, and native Kubernetes alignment for Linux servers.
- The CLI compatibility between Docker and Podman enables near-zero friction migration if you want to switch.
Both tools do the same job — run containers. Docker got there first and built a polished platform around it. Podman arrived later with a cleaner architecture that better reflects how containers should work on Linux in the first place.
If you’re on a Mac with Docker Desktop, stick with Docker for now. If you’re on Linux and want something simpler to manage, Podman is worth the switch.