- Authors
- Name
- 1. Introduction: The Light and Shadow of Docker, and the Shift to Podman
- 2. [Innovation 1] Daemonless Architecture: The End of Single Points of Failure
- 3. [Innovation 2] Rootless Security: A Paradigm Shift in Privileges
- 4. [Innovation 3] play kube: "Unifying the Language of Development and Operations"
- 5. [Innovation 4] Pod-Native Management: A True Pod Manager
- 6. [Innovation 5] Pasta Networking: NAT-Free Rootless Networking
- 7. Podman Desktop: Podman's Full Experience Through a GUI
- 8. From Docker to Podman: Practical Migration Guide
- 9. Podman vs Docker: Comprehensive Comparison
- 10. Conclusion: The Future of Containers Lies in Subtraction
- 11. References
1. Introduction: The Light and Shadow of Docker, and the Shift to Podman
1.1 The World Docker Changed
In 2013, a 5-minute demo by Solomon Hykes of dotCloud at PyCon completely reshaped the software industry landscape. It sent the developer's eternal excuse — "It works on my machine" — to the dustbin of history. Docker made Linux containers (LXC) accessible to everyone, and with the trinity of image layering, Dockerfile, and Docker Hub, it became the de facto standard of the container ecosystem.
But every monopolistic standard carries structural vulnerabilities.
1.2 The Turning Point of Docker Desktop Licensing
In August 2021, Docker Inc. changed the Docker Desktop licensing policy. Companies with 250 or more employees or annual revenue exceeding $10 million are now required to have a paid subscription (Pro, Team, Business). This change carries significance beyond a simple pricing policy:
- Supply chain dependency: A critical development tool became subject to a single vendor's licensing policy
- Compliance risk: Unauthorized use creates legal exposure
- Accelerated search for alternatives: Red Hat, SUSE, Google, and others increased investment in alternative ecosystems
1.3 Software Supply Chain Security as an Imperative
From the 2020 SolarWinds hack, the 2021 Log4Shell, to the 2024 XZ Utils backdoor, Software Supply Chain Security has become mandatory, not optional. Container runtimes sit at the lowest layer of this supply chain, and their security model determines the trust foundation of the entire stack.
Docker daemon running with root privileges became a fundamental subject of re-evaluation in this context.
1.4 The Shift to Podman
At the intersection of these three trends — licensing risk, supply chain security, and architectural limitations — Podman, led by Red Hat and built with the open-source community, is emerging.
Podman is not simply a "Docker replacement." It is a project that redesigns the fundamental premises of container technology. As of 2026, the Podman 5.x series supports TLS/mTLS-based remote connection encryption, stabilized OCI Artifact management, native macOS Hypervisor Framework integration, and multi-platform builds via podman farm build, maturing into an enterprise-grade container platform.
This article provides an in-depth analysis of the 5 fundamental innovations Podman brings, from an architect's perspective:
| Innovation | Key Theme | Problem Solved |
|---|---|---|
| Innovation 1 | Daemonless Architecture | Eliminates single point of failure (SPOF) |
| Innovation 2 | Rootless Security | Eradicates container escape risk |
| Innovation 3 | play kube | Resolves dev/ops language duality |
| Innovation 4 | Pod-Native Management | Kubernetes-native workflow |
| Innovation 5 | Pasta Networking | Removes NAT overhead, performance leap |
2. [Innovation 1] Daemonless Architecture: The End of Single Points of Failure
2.1 The Structural Problem of Docker Daemon
To understand Docker's architecture, you must understand dockerd (Docker Daemon) at its center. All Docker CLI commands are passed to dockerd via REST API, which calls containerd, which in turn creates actual containers through runc.
+-------------------------------------------------------------+
| Docker Architecture (Client-Server) |
| |
| +----------+ REST API +------------------------+ |
| | docker | ----------------->| dockerd | |
| | CLI | /var/run/ | (Docker Daemon) | |
| +----------+ docker.sock | +------------------+ | |
| | | containerd | | |
| +----------+ REST API | | +------------+ | | |
| | docker | ----------------->| | | runc | | | |
| | CLI | /var/run/ | | +------------+ | | |
| +----------+ docker.sock | +------------------+ | |
| | | |
| +----------+ REST API | +-----+ +-----+ | |
| | docker | ----------------->| | C1 | | C2 | ... | |
| | CLI | | +-----+ +-----+ | |
| +----------+ +------------------------+ |
| ^ |
| | |
| * Single Point of Failure * |
| If dockerd dies, |
| all container management lost |
+-------------------------------------------------------------+
Three core problems with this structure:
First, Single Point of Failure (SPOF): If dockerd terminates abnormally, management of all running containers becomes impossible. The containers themselves continue running, but all management functions — log collection, health checks, resource limit changes — are completely lost.
Second, Root privilege requirement: dockerd runs with root privileges by default. Any user with access to /var/run/docker.sock effectively has root-equivalent privileges. This is a classic Privilege Escalation attack vector.
Third, Resource overhead: Even when no containers are running, dockerd and containerd occupy memory in standby mode.
2.2 Podman's Fork-Exec Model
Podman solved all these problems by eliminating the daemon entirely. The name Podman itself is short for Pod Manager, and its architecture is built on a fundamentally different philosophy.
+-------------------------------------------------------------+
| Podman Architecture (Daemonless Fork-Exec) |
| |
| +----------+ fork/exec +-------+ +--------------+ |
| | podman | -------------->| conmon |--->| Container 1 | |
| | CLI | +-------+ +--------------+ |
| +----------+ |
| | |
| | fork/exec +-------+ +--------------+ |
| +--------------------->| conmon |--->| Container 2 | |
| | +-------+ +--------------+ |
| | |
| | fork/exec +-------+ +--------------+ |
| +--------------------->| conmon |--->| Container 3 | |
| +-------+ +--------------+ |
| |
| * No daemon: each container is an independent process |
| * Exiting Podman CLI does not affect containers |
| * conmon: lightweight container monitor (logs, exit codes) |
| * Direct OCI runtime (crun/runc) invocation |
+-------------------------------------------------------------+
The process flow when Podman runs a container:
- The user executes
podman run - The Podman CLI directly fork/execs the OCI runtime (default:
crun, orrunc) conmon(Container Monitor) is created to manage the container process's stdout/stderr and exit code- The Podman CLI process finishes its role and exits
- The container continues running as an independent process
"The best daemon is no daemon at all. Podman proves that container management doesn't need a privileged, always-on service — it needs a tool that does its job and gets out of the way." — Dan Walsh, Red Hat Container Security Architect
2.3 systemd Integration: Containers as First-Class Citizens of the OS
One of the most powerful advantages of Podman's daemonless architecture is its natural integration with systemd. In Docker, a separate service manager called dockerd managed container lifecycles, but in Podman, Linux's PID 1, systemd, directly manages containers.
This means containers have been promoted to first-class citizens of the operating system:
- Auto-start: Containers start automatically on system boot
- Dependency management: Leveraging systemd's dependency graph with
After=network-online.target, etc. - Logging integration: Unified container log viewing via
journalctl - Resource management: Resource allocation through systemd cgroup slices
- Health checks: Precise health monitoring based on
WatchdogSec=
# /etc/containers/systemd/webapp.container (Quadlet format)
[Unit]
Description=Production Web Application
After=network-online.target
Wants=network-online.target
[Container]
Image=registry.example.com/webapp:v2.1.0
PublishPort=8080:8080
Environment=NODE_ENV=production
Environment=DB_HOST=db.internal
Volume=/data/webapp:/app/data:Z
Network=webapp.network
AutoUpdate=registry
HealthCmd=/bin/curl -f http://localhost:8080/health || exit 1
HealthInterval=30s
HealthRetries=3
HealthStartPeriod=10s
[Service]
Restart=always
RestartSec=5
TimeoutStartSec=120
WatchdogSec=60
[Install]
WantedBy=multi-user.target default.target
2.4 Quadlet: The Definitive systemd-Native Container Management
Quadlet, introduced in Podman 4.4, is an innovative approach that defines containers as extensions of systemd unit files. Instead of the long command lines generated by the legacy podman generate systemd, it manages containers through declarative unit files with .container, .volume, .network, .pod, .kube, and .build extensions.
Key Benefits of Quadlet:
| Category | Legacy (generate systemd) | Quadlet |
|---|---|---|
| File format | Auto-generated long ExecStart | Declarative .container files |
| Maintenance | Regeneration needed on image update | Declarative edit + daemon-reload |
| Readability | All options on a single command line | Structured by section |
| File location (root) | /etc/systemd/system/ | /etc/containers/systemd/ |
| File location (rootless) | ~/.config/systemd/user/ | ~/.config/containers/systemd/ |
Multi-container stack example — database and application:
# ~/.config/containers/systemd/app-network.network
[Network]
Subnet=10.89.1.0/24
Gateway=10.89.1.1
# ~/.config/containers/systemd/postgres.container
[Unit]
Description=PostgreSQL Database
[Container]
Image=docker.io/library/postgres:16-alpine
Volume=pgdata.volume:/var/lib/postgresql/data:Z
Network=app-network.network
Environment=POSTGRES_DB=appdb
Environment=POSTGRES_USER=appuser
Secret=pg-password,type=env,target=POSTGRES_PASSWORD
HealthCmd=pg_isready -U appuser
HealthInterval=10s
[Service]
Restart=always
[Install]
WantedBy=multi-user.target default.target
# ~/.config/containers/systemd/pgdata.volume
[Volume]
# Apply Quadlet
systemctl --user daemon-reload
# Start service
systemctl --user start postgres.service
# Check status
systemctl --user status postgres.service
# View logs
journalctl --user -u postgres.service -f
Quadlet perfectly embodies the spirit of Infrastructure as Code (IaC) in container management. Deploy .container files with tools like Ansible or Terraform, and without a separate container orchestrator, systemd itself becomes the container orchestrator.
3. [Innovation 2] Rootless Security: A Paradigm Shift in Privileges
3.1 Container Escape: Why Root Is Dangerous
In Docker's default execution model, root (UID 0) inside a container shares the same UID as root (UID 0) on the host. Of course, security layers like Linux Capabilities, seccomp, and AppArmor/SELinux restrict this, but every time a kernel vulnerability is discovered, these restrictions can be bypassed.
Notable container escape cases:
| CVE | Vulnerability | Impact |
|---|---|---|
| CVE-2019-5736 | runc vulnerability | Overwrite host runc binary from container |
| CVE-2020-15257 | containerd Shim API | Access host network namespace |
| CVE-2022-0185 | Kernel heap overflow | Gain host root from unprivileged container |
| CVE-2024-21626 | runc file descriptor leak | Access host filesystem from container |
The common thread in all these vulnerabilities is the exploitation of the premise that container processes run with root privileges on the host.
3.2 User Namespace and UID Mapping: Podman's Answer
Podman's rootless mode uses Linux User Namespace to neutralize this premise entirely. The core principle is simple: map UID 0 (root) inside the container to an unprivileged UID on the host.
+-------------------------------------------------------------+
| User Namespace UID Mapping Diagram |
| |
| +---------------------+ +---------------------------+ |
| | Inside Container | | Host System | |
| | (User Namespace) | | | |
| | | | | |
| | UID 0 (root) ----+------+--> UID 1000 (youruser) | |
| | UID 1 ----+------+--> UID 100000 | |
| | UID 2 ----+------+--> UID 100001 | |
| | UID 3 ----+------+--> UID 100002 | |
| | ... | | ... | |
| | UID 65535 ----+------+--> UID 165535 | |
| | | | | |
| +---------------------+ +---------------------------+ |
| |
| * Even if container root escapes, it's UID 1000 on host |
| * Unprivileged user -> no access to system files |
| * Kernel exploit scope drastically reduced |
+-------------------------------------------------------------+
This mapping is configured via /etc/subuid and /etc/subgid files:
# /etc/subuid - subordinate UID range per user
youruser:100000:65536
# /etc/subgid - subordinate GID range per user
youruser:100000:65536
# Meaning: youruser can use 65536 UIDs starting from UID 100000
How to verify the actual mapping:
# Run a container in rootless mode
podman run -d --name test-rootless alpine sleep 3600
# Check UID inside the container
podman exec test-rootless id
# uid=0(root) gid=0(root) <- Appears as root inside container
# Check actual UID on the host
podman top test-rootless huser
# HUSER
# 1000 <- Regular user (UID 1000) on the host
3.3 User Namespace Modes: Fine-Grained Security Control
Podman supports various User Namespace modes through the --userns option:
| Mode | Description | Use Case |
|---|---|---|
auto | Automatic UID/GID range allocation (default) | General rootless containers |
keep-id | Maps host user UID identically inside the container | Preserving bind mount file permissions |
nomap | Does not map host user UID to the container | Maximum security isolation |
host | Disables User Namespace | Legacy compatibility (not recommended) |
# keep-id mode: Maintain host UID inside the container
# -> Solves bind-mounted file ownership issues
podman run --userns=keep-id -v $HOME/project:/workspace:Z \
-it node:20-alpine sh
# nomap mode: Does not map host user UID at all
# -> Container cannot access host user-owned files (except world permissions)
podman run --userns=nomap alpine cat /proc/self/uid_map
3.4 Compliance: PCI-DSS, FedRAMP, NIST Regulatory Adherence
Rootless containers provide decisive advantages beyond technical improvements in terms of compliance:
PCI-DSS v4.0 (Payment Card Industry Data Security Standard):
- Requirement 7.2: "Restrict access to system components according to the principle of least privilege"
- Rootless containers operate without root privileges, structurally satisfying this requirement
FedRAMP (Federal Risk and Authorization Management Program):
- AC-6 (Least Privilege): "Grant only the minimum privileges required for system functions"
- Podman's rootless mode is the technical implementation of this control itself
NIST SP 800-190 (Application Container Security Guide):
- "Run containers as non-root users to reduce risks to the host operating system"
- Podman implements this as default behavior
Linux Capabilities Comparison — Docker vs Podman:
Podman grants only 11 kernel Capabilities to containers by default, fewer than Docker's 14. This is a concrete implementation of the Principle of Least Privilege:
# Check Podman default Capabilities
podman run --rm alpine grep Cap /proc/1/status
# CapBnd: 00000000a80425fb
# Check Docker default Capabilities
docker run --rm alpine grep Cap /proc/1/status
# CapBnd: 00000000a80435fb
# -> Docker grants more Capabilities (includes NET_RAW, etc.)
Security Architect's Principle: "Security is not about adding things — it's about removing what's unnecessary." Podman's rootless model is a textbook implementation of this principle.
4. [Innovation 3] play kube: "Unifying the Language of Development and Operations"
4.1 Docker Compose vs Kubernetes YAML: The Pain of Duality
One of the most common frictions developers face in cloud native environments is the language for local development and production deployment being different.
+-------------------------------------------------------------+
| Dev/Ops Language Duality Problem |
| |
| Developer Laptop Production Cluster |
| +-------------------+ +--------------------+ |
| | docker-compose.yml| | deployment.yaml | |
| | | -------> | service.yaml | |
| | version: "3.8" | Translation| configmap.yaml | |
| | services: | Required | ingress.yaml | |
| | web: | | | |
| | image: app | | apiVersion: apps/v1| |
| | ports: | | kind: Deployment | |
| | - "8080:80" | | spec: | |
| +-------------------+ | replicas: 3 | |
| +--------------------+ |
| |
| * Two syntaxes, two tools, double the learning cost |
| * Deployment failures due to subtle conversion differences |
| * A new variant of "It worked on my laptop..." |
+-------------------------------------------------------------+
Tools like Kompose and Docker Compose to Kubernetes converters exist, but they are adding another layer on top of the problem, not a fundamental solution.
4.2 play kube: Running Kubernetes YAML Locally
Podman's play kube (previously podman play kube, now podman kube play) eliminates this duality at its source. It runs Kubernetes YAML files as-is in your local Podman environment.
Supported Kubernetes resource types:
- Pod
- Deployment
- DaemonSet
- ConfigMap
- Secret
- PersistentVolumeClaim
Step-by-Step: Running a Microservices Stack with play kube
Step 1 — Write Kubernetes YAML (microservices-stack.yaml):
# microservices-stack.yaml
apiVersion: v1
kind: ConfigMap
metadata:
name: app-config
data:
DATABASE_HOST: 'postgres'
DATABASE_PORT: '5432'
DATABASE_NAME: 'appdb'
APP_LOG_LEVEL: 'info'
---
apiVersion: v1
kind: Pod
metadata:
name: microservices
labels:
app: microservices-stack
environment: development
spec:
# Init container: database migration
initContainers:
- name: db-migration
image: docker.io/library/flyway/flyway:10
command: ['flyway', 'migrate']
env:
- name: FLYWAY_URL
value: 'jdbc:postgresql://localhost:5432/appdb'
- name: FLYWAY_USER
value: 'appuser'
- name: FLYWAY_PASSWORD
value: 'secret123'
volumeMounts:
- name: migration-scripts
mountPath: /flyway/sql
containers:
# Database
- name: postgres
image: docker.io/library/postgres:16-alpine
ports:
- containerPort: 5432
env:
- name: POSTGRES_DB
value: 'appdb'
- name: POSTGRES_USER
value: 'appuser'
- name: POSTGRES_PASSWORD
value: 'secret123'
volumeMounts:
- name: pgdata
mountPath: /var/lib/postgresql/data
# API Server
- name: api-server
image: docker.io/library/node:20-alpine
ports:
- containerPort: 3000
hostPort: 3000
env:
- name: DATABASE_HOST
value: 'localhost'
- name: DATABASE_PORT
value: '5432'
command: ['node', 'server.js']
volumeMounts:
- name: app-source
mountPath: /app
# Redis Cache
- name: redis
image: docker.io/library/redis:7-alpine
ports:
- containerPort: 6379
command: ['redis-server', '--maxmemory', '256mb']
volumes:
- name: pgdata
persistentVolumeClaim:
claimName: pgdata-pvc
- name: migration-scripts
hostPath:
path: ./migrations
- name: app-source
hostPath:
path: ./src
Step 2 — Run the stack:
# Create the entire stack from the YAML file
podman kube play microservices-stack.yaml
# Check running status
podman pod ps
# POD ID NAME STATUS CREATED INFRA ID # OF CONTAINERS
# a1b2c3d4e5f6 microservices Running 10 seconds ago 9f8e7d6c5b4a 4
# Check individual containers
podman ps --pod
# CONTAINER ID IMAGE STATUS POD ID NAMES
# ... docker.io/library/postgres:16 Up 10 seconds a1b2c3d4e5f6 microservices-postgres
# ... docker.io/library/node:20 Up 8 seconds a1b2c3d4e5f6 microservices-api-server
# ... docker.io/library/redis:7 Up 8 seconds a1b2c3d4e5f6 microservices-redis
# Check API server logs
podman logs microservices-api-server
Step 3 — Tear down the stack:
# Tear down and clean up the entire stack with the --down flag
podman kube play --down microservices-stack.yaml
# Verify
podman pod ps
# (empty - all resources cleaned up)
4.3 Convention-Based Image Build
One of the hidden powerful features of play kube is convention-based automatic image building. If a directory with the same name as an image referenced in the YAML exists in the current path, and it contains a Containerfile or Dockerfile, it builds automatically.
Project directory structure:
+-- my-stack.yaml <- references image: my-api
+-- my-api/ <- auto-build target
| +-- Containerfile
| +-- src/
| +-- server.js
+-- my-frontend/ <- references image: my-frontend
+-- Containerfile
+-- dist/
+-- index.html
# Auto-build images + run with --build flag
podman kube play --build my-stack.yaml
# Replace already-built images (force build)
podman kube play --replace --build my-stack.yaml
This feature enables running CI/CD pipelines and local development environments with the same YAML, bringing Podman's vision of "Develop locally, deploy globally" to life.
4.4 Init Containers: Always and Once
Podman faithfully implements the Kubernetes Init Container concept locally. The Podman-specific always and once type distinction is particularly useful in practice:
| Type | Behavior | Use Case |
|---|---|---|
once (default) | Runs once at Pod start then removed | DB migration, secret initialization |
always | Runs every time Pod starts (not removed) | Health check waiting, config refresh |
# Specifying Init container type (Podman annotation)
metadata:
annotations:
io.podman.annotations.init.container.type/wait-for-db: 'always'
spec:
initContainers:
- name: wait-for-db
image: docker.io/library/busybox:latest
command:
['sh', '-c', 'until nc -z localhost 5432; do echo "Waiting for DB..."; sleep 2; done']
always-type Init containers are also included in podman kube generate output, so initialization logic validated locally can be carried directly to the Kubernetes cluster.
5. [Innovation 4] Pod-Native Management: A True Pod Manager
5.1 Podman's Essence: A Tool That Manages Pods
Podman's name comes from Pod Manager. This is not just clever naming — it expresses the design intent to implement the Kubernetes Pod concept as a first-class citizen in the local environment.
In Docker, running multiple containers together requires Docker Compose, a separate tool, but in Podman, Pods are a built-in feature.
+-------------------------------------------------------------+
| Podman Pod Architecture |
| |
| +--- Pod ------------------------------------------------+ |
| | | |
| | +----------+ | |
| | | Infra | <- pause container | |
| | |Container | <- network namespace owner | |
| | | (pause) | <- Pod lifecycle anchor | |
| | +----------+ | |
| | | | |
| | |-- Shared: Network Namespace | |
| | |-- Shared: IPC Namespace | |
| | |-- Shared: UTS Namespace (hostname) | |
| | | | |
| | +----------+ +----------+ +----------+ | |
| | | App | | Sidecar | | Logging | | |
| | |Container | | (envoy) | | (fluentd)| | |
| | | | | | | | | |
| | | :8080 | | :15001 | | :24224 | | |
| | +----------+ +----------+ +----------+ | |
| | | |
| | * All containers communicate via localhost | |
| | * Same network model as Kubernetes Pods | |
| +--------------------------------------------------------+ |
| |
+-------------------------------------------------------------+
Key characteristics of Podman Pods:
- Infra container: Same role as Kubernetes
pausecontainer. Owns the network namespace, shared by all containers in the Pod - localhost communication: Containers within a Pod communicate via
localhost(127.0.0.1) - Namespace sharing: Network, IPC, and UTS namespaces shared by default
- Batch management: Pod-level lifecycle management via
podman pod start/stop/rm
5.2 Implementing the Sidecar Pattern
The most widely used Sidecar Pattern in cloud native architecture can be perfectly implemented locally with Podman Pods:
# 1. Create Pod (port mapping is set at the Pod level)
podman pod create --name my-app \
--publish 8080:8080 \
--publish 9090:9090
# 2. Main application container
podman run -d --pod my-app \
--name app-main \
my-registry.io/my-app:v1.0
# 3. Sidecar: Envoy proxy
podman run -d --pod my-app \
--name app-envoy \
docker.io/envoyproxy/envoy:v1.31
# 4. Sidecar: Log collector
podman run -d --pod my-app \
--name app-logger \
docker.io/fluent/fluentd:v1.17
# 5. Sidecar: Metrics collector
podman run -d --pod my-app \
--name app-metrics \
docker.io/prom/node-exporter:v1.8
# Check Pod status
podman pod inspect my-app
This pattern allows you to test service mesh configurations destined for Kubernetes (Envoy + app + logging) in your local development environment with the same network topology.
5.3 generate kube: The Bridge from Local to Cluster
Podman's generate kube (now podman kube generate) supports the reverse workflow of exporting locally running containers or Pods to Kubernetes YAML.
Complete Workflow Example:
# Step 1: Configure Pod locally
podman pod create --name webapp --publish 8080:80
podman run -d --pod webapp \
--name webapp-nginx \
-v ./html:/usr/share/nginx/html:Z \
docker.io/library/nginx:alpine
podman run -d --pod webapp \
--name webapp-redis \
docker.io/library/redis:7-alpine
# Step 2: Generate Kubernetes YAML
podman kube generate webapp -f webapp-k8s.yaml
# Step 3: Inspect generated YAML
cat webapp-k8s.yaml
Generated YAML example:
# webapp-k8s.yaml (auto-generated by podman kube generate)
apiVersion: v1
kind: Pod
metadata:
creationTimestamp: '2026-03-01T09:00:00Z'
labels:
app: webapp
name: webapp
spec:
containers:
- name: webapp-nginx
image: docker.io/library/nginx:alpine
ports:
- containerPort: 80
hostPort: 8080
protocol: TCP
volumeMounts:
- mountPath: /usr/share/nginx/html
name: html-host
- name: webapp-redis
image: docker.io/library/redis:7-alpine
volumes:
- name: html-host
hostPath:
path: /home/user/project/html
type: Directory
# Step 4: Apply generated YAML to Kubernetes cluster
kubectl apply -f webapp-k8s.yaml
# Or run the same YAML on another Podman host
podman kube play webapp-k8s.yaml
Additional generate kube options:
# Generate Service object as well
podman kube generate -s webapp -f webapp-with-service.yaml
# Generate as Deployment type (specify replicas)
podman kube generate --type deployment --replicas 3 webapp
# Can also generate as DaemonSet or Job type
podman kube generate --type job webapp
This bidirectional workflow (play kube <-> generate kube) is the core Podman innovation that makes "Develop locally, deploy globally" technically possible:
+-----------------------------------------------------------+
| Podman <-> Kubernetes Bidirectional Workflow |
| |
| Local Development Kubernetes Cluster |
| +---------+ +-------------+ |
| | podman | podman kube generate| kubectl | |
| | pod | --------------------->| apply -f | |
| | create | | | |
| | + run | podman kube play | Deployment | |
| | | <---------------------| export | |
| +---------+ +-------------+ |
| |
| * Same YAML, same semantics |
| * No translation needed between environments |
+-----------------------------------------------------------+
6. [Innovation 5] Pasta Networking: NAT-Free Rootless Networking
6.1 The Rootless Networking Dilemma
One of the biggest technical challenges of rootless containers is networking. Regular users do not have permission to create network interfaces or manipulate iptables rules. The technology Podman first adopted to solve this was slirp4netns.
How slirp4netns works:
slirp4netns emulates a TCP/IP stack in user space. All network traffic from the container follows this path:
Container -> TAP device -> slirp4netns (user-space NAT) -> Host network
This approach works, but since every packet must undergo NAT translation in user space, it incurs significant latency and CPU overhead.
6.2 Pasta: A Paradigm Shift
Pasta (Pack A Subtle Tap Abstraction) is part of the passt project, a next-generation rootless networking solution that replaces slirp4netns's NAT-based approach with L2-L4 socket mapping.
It was adopted as the default rootless network driver starting with Podman 5.0, and is also set as the default in RHEL 9.5.
+-------------------------------------------------------------+
| slirp4netns vs Pasta Architecture Comparison |
| |
| +- slirp4netns (Legacy) ----------------------------------+|
| | ||
| | Container User Space Host ||
| | +-------+ +------------------+ +----------+ ||
| | | eth0 |--->| slirp4netns |--->| Host | ||
| | | (TAP) | | +------------+ | | Network | ||
| | +-------+ | | User-space | | +----------+ ||
| | | | TCP/IP | | ||
| | | | + NAT | | <- All packets NAT'd||
| | | +------------+ | <- High CPU overhead ||
| | +------------------+ ||
| +----------------------------------------------------------+|
| |
| +- Pasta (Podman 5.0+ default) ---------------------------+|
| | ||
| | Container Kernel Space Host ||
| | +-------+ +------------------+ +----------+ ||
| | | eth0 |--->| L2->L4 socket |--->| Host | ||
| | | (TAP) | | mapping | | Network | ||
| | +-------+ | +------------+ | | cfg copy | ||
| | | | splice(2) | | +----------+ ||
| | | | sendmmsg() | | ||
| | | | recvmmsg() | | ||
| | | +------------+ | <- No NAT ||
| | +------------------+ <- Direct socket fwd ||
| +----------------------------------------------------------+|
| |
+-------------------------------------------------------------+
Pasta's Core Technical Innovations:
- Host network configuration copying: Copies the host's network config directly to the container namespace, making NAT itself unnecessary
- L2-L4 socket mapping: Directly maps Layer-2 TAP interface packets to Layer-4 sockets (TCP/UDP/ICMP)
- splice(2) system call: Uses the kernel's
splice(2)system call for zero-copy data transfer on local connections - recvmmsg/sendmmsg optimization: Processes multiple packets in a single system call, reducing syscall overhead
- Tap Bypass Path: Packets destined for local addresses skip L2 conversion and transfer directly between L4 sockets
6.3 Performance Comparison: slirp4netns vs Pasta
| Metric | slirp4netns | Pasta | Improvement |
|---|---|---|---|
| Container start time | ~1.2s | ~0.8s | 33% faster |
| Memory usage | ~100MB | ~85MB | 15% less |
| TCP throughput (1 conn) | Baseline | +15-30% | Significant |
| TCP latency (p50) | Baseline | -20-40% | Major improvement |
| UDP throughput | Baseline | +10-20% | Improved |
| Parallel connections (8 or fewer) | Baseline | Advantage | Pasta wins |
| Parallel connections (more than 8) | Advantage | Baseline | slirp4netns wins* |
| CPU overhead (idle) | High | Low | Significant |
*Note: In high-parallelism scenarios (parallelism more than 8), slirp4netns may show better performance. This is because Pasta's socket mapping approach can cause contention under extremely high concurrency. However, for typical development environments and most production workloads, Pasta is generally superior.
6.4 Pasta Networking Configuration and Usage
# Pasta is the default in Podman 5.0+
# To explicitly specify:
podman run --network=pasta -d nginx
# To fall back to slirp4netns:
podman run --network=slirp4netns -d nginx
# Pasta port forwarding (expose specific ports only)
podman run --network=pasta:-T,8080,-U,5353 -d my-app
# Check Pasta networking status
podman inspect --format '{{.HostConfig.NetworkMode}}' <container>
6.5 Network Mode Selection Guide
| Scenario | Recommended Network Mode | Reason |
|---|---|---|
| General rootless development | pasta (default) | Best performance/compatibility balance |
| Legacy compatibility needed | slirp4netns | Stable on older kernels |
| Root containers | bridge (CNI/netavark) | Traditional bridge in root mode |
| Direct host network access | host | When network isolation not needed |
| Inter-container communication in Pod | pod shared namespace | localhost communication |
7. Podman Desktop: Podman's Full Experience Through a GUI
7.1 Podman Desktop Overview
2025 was a decisive year for Podman Desktop. It surpassed 3 million downloads, solidifying its position as an alternative to Docker Desktop. In January 2026, it was also listed as a CNCF (Cloud Native Computing Foundation) project.
Podman Desktop Key Features:
| Feature | Description |
|---|---|
| Container management | Create, start, stop, delete, view logs, terminal access |
| Image management | Build, pull, push, search, prune (unused image cleanup) |
| Pod management | Pod creation, Kubernetes YAML deployment, Pod log search |
| Volumes/Networks | Volume create/delete, network visibility and management |
| Kubernetes integration | Kind, Minikube, OpenShift cluster integration |
| Extension ecosystem | BootC, Headlamp, AI Lab extensions |
| Compose support | Docker Compose file compatible execution |
| Dashboard customization | Widget placement, table column customization |
7.2 Enterprise Podman Desktop
Red Hat has released Red Hat build of Podman Desktop with features tailored for enterprise environments:
- Fleet-wide Settings: Organization-level settings management for registry mirrors, HTTP proxies, etc.
- OpenShift integration: Direct OpenShift cluster deployment support
- BootC workflow: Converting containers to bootable OS images
- Security policy enforcement: Deploying default settings aligned with enterprise security standards
8. From Docker to Podman: Practical Migration Guide
8.1 Why Migration Is Simple
Podman is designed to maintain command-level compatibility with the Docker CLI. Most docker commands can be directly replaced with podman.
8.2 Command Mapping Table
| Docker Command | Podman Command | Difference |
|---|---|---|
docker run | podman run | Identical |
docker build | podman build | Identical |
docker pull | podman pull | Identical |
docker push | podman push | Identical |
docker ps | podman ps | Identical |
docker exec | podman exec | Identical |
docker logs | podman logs | Identical |
docker images | podman images | Identical |
docker compose | podman compose | Separate install or podman-compose |
docker swarm | Not supported | Use Kubernetes instead |
| N/A | podman pod | Podman-exclusive feature |
| N/A | podman kube play | Podman-exclusive feature |
| N/A | podman kube generate | Podman-exclusive feature |
8.3 Step-by-Step Migration
Step 1 — Installation:
# macOS
brew install podman
podman machine init --cpus 4 --memory 8192 --disk-size 60
podman machine start
# RHEL/CentOS/Fedora
sudo dnf install -y podman
# Ubuntu/Debian
sudo apt-get install -y podman
# Windows
winget install RedHat.Podman
Step 2 — Docker compatibility alias setup (optional):
# ~/.bashrc or ~/.zshrc
alias docker=podman
# Docker Socket compatibility (for tools that use the Docker API)
# macOS:
podman machine stop
podman machine set --rootful
podman machine start
# Linux (rootless):
systemctl --user enable --now podman.socket
export DOCKER_HOST=unix://$XDG_RUNTIME_DIR/podman/podman.sock
Step 3 — Docker Compose file migration:
# Method 1: podman compose (Podman 5.x built-in support)
podman compose up -d
# Method 2: podman-compose (Python-based standalone tool)
pip install podman-compose
podman-compose up -d
# Method 3: Convert to Kubernetes YAML (recommended)
# After converting docker-compose.yml to Kubernetes YAML
podman kube play my-stack.yaml
Step 4 — CI/CD pipeline update:
# GitHub Actions example
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Install Podman
run: |
sudo apt-get update
sudo apt-get install -y podman
- name: Build Image
run: podman build -t my-app:${{ github.sha }} .
- name: Push to Registry
run: |
podman login -u ${{ secrets.REGISTRY_USER }} \
-p ${{ secrets.REGISTRY_PASS }} \
registry.example.com
podman push my-app:${{ github.sha }} \
registry.example.com/my-app:${{ github.sha }}
8.4 Migration Considerations
| Area | Docker | Podman | Mitigation |
|---|---|---|---|
| Daemon state | Always running | No daemon | Configure auto-start via systemd |
| Docker Socket | /var/run/docker.sock | Podman Socket must be enabled | Enable podman.socket service |
| Docker Swarm | Supported | Not supported | Migrate to Kubernetes |
| Image storage | /var/lib/docker | ~/.local/share/containers (rootless) | Existing images must be re-pulled |
| Network driver | bridge (dockerd managed) | Netavark + Pasta (rootless) | Compatible, verify configuration |
| Build cache | Docker BuildKit | Podman Buildah built-in | Compatible |
9. Podman vs Docker: Comprehensive Comparison
| Comparison Item | Docker | Podman |
|---|---|---|
| Architecture | Client-server (dockerd daemon) | Daemonless (Fork-Exec) |
| Default execution mode | root (rootless is experimental) | rootless (default) |
| Pod support | None (Compose as substitute) | Native Pod support |
| Kubernetes YAML execution | Requires separate tools | podman kube play built-in |
| systemd integration | Limited | Native via Quadlet |
| Container start time | ~1.2s | ~0.8s |
| Idle memory | ~100MB (includes daemon) | ~85MB |
| Idle CPU | Daemon always running | 0% (no daemon) |
| Kernel Capabilities | 14 (default) | 11 (default) |
| OCI compliance | Fully compliant | Fully compliant |
| Image compatibility | Docker Hub default | Docker Hub + all OCI registries |
| Build tool | BuildKit | Buildah built-in |
| Rootless networking | slirp4netns | Pasta (default) |
| Docker Compose | Native | Compatible (podman compose) |
| Docker Swarm | Supported | Not supported (Kubernetes recommended) |
| GUI tool | Docker Desktop (paid*) | Podman Desktop (free/open-source) |
| License | Apache 2.0 (Engine) / Paid (Desktop) | Apache 2.0 (entirely free) |
| Multi-platform build | buildx | podman farm build |
| Secret management | Docker Secrets (Swarm) | podman secret |
| TLS/mTLS remote access | Supported | Enhanced in 5.x |
| OCI Artifact management | Limited | podman artifact (5.x stabilized) |
*Docker Desktop: Paid subscription required for companies with 250+ employees or annual revenue exceeding $10M
10. Conclusion: The Future of Containers Lies in Subtraction
Looking back at the 5 innovations Podman presents, a consistent philosophy runs through them all:
10.1 Security as the Default
In Docker, security was something you "added." You had to enable rootless mode, write seccomp profiles, and configure AppArmor policies. In Podman, security is "already on." Rootless is the default, there is no daemon, and minimal Capabilities are applied by default.
Security through subtraction: Removing the daemon (daemonless), removing root (rootless), removing unnecessary privileges (minimal Capabilities). Added security gets forgotten, but security provided by default always works.
10.2 Unifying the Language of Development and Operations
The duality of developing with Docker Compose and deploying with Kubernetes YAML has imposed an invisible tax — the translation cost — on development organizations.
Podman's play kube and generate kube eliminate this tax. They realize the Single Language Principle where the YAML developers write becomes the YAML for the production environment.
10.3 Resource Efficiency
A daemonless architecture means zero CPU usage at idle; Pasta networking means NAT overhead elimination; systemd integration means savings on the resource cost of a separate orchestrator. While this may be hard to notice on a single development machine, it makes a meaningful difference across hundreds of edge nodes, resource-constrained IoT environments, and cost-optimized cloud infrastructure.
10.4 Final Summary
+-------------------------------------------------------------+
| Podman's 5 Innovations Summary |
| |
| +-------------+ Removed the daemon -> Stability |
| | Innovation 1| Eliminated SPOF -> Container autonomy|
| | Daemonless | systemd integration -> OS-native mgmt |
| +-------------+ |
| |
| +-------------+ Removed root -> Minimized attack surface |
| | Innovation 2| User Namespace -> UID isolation |
| | Rootless | Minimal Capabilities-> Compliance |
| +-------------+ |
| |
| +-------------+ Removed translation -> Dev/Ops language unity |
| | Innovation 3| Direct K8s YAML exec-> Cross-env consistency |
| | play kube | Convention builds -> Workflow simplification |
| +-------------+ |
| |
| +-------------+ Removed abstraction -> Same unit as K8s |
| | Innovation 4| Native Pods -> Local sidecar patterns |
| | Pod mgmt | generate kube -> Bidirectional workflow |
| +-------------+ |
| |
| +-------------+ Removed NAT -> Reduced latency |
| | Innovation 5| L2-L4 socket mapping-> Increased throughput |
| | Pasta | splice(2) -> Zero-copy transfer |
| +-------------+ |
| |
| * Common philosophy: Maximize essential value by removing |
| the unnecessary |
+-------------------------------------------------------------+
Podman does not deny Docker. Built on the foundation of the container revolution Docker started, it redesigns container technology to meet modern requirements of security, standards compliance, and resource efficiency.
In the 2026 cloud native landscape, the criterion for choosing a container tool should not be "what can it do" but "what does it not do." Not running an unnecessary daemon, not granting unnecessary privileges, and not adding an unnecessary translation layer. That is the future of containers that Podman envisions.
11. References
Official Documentation
- Podman Official Documentation
- Podman GitHub Release Notes
- Podman Desktop Official Site
- Podman Quadlet Documentation
- podman-kube-play Documentation
- podman-kube-generate Documentation
- passt/pasta Project
Red Hat Blogs and Technical Documentation
- Podman 5.0 Unveiled — Red Hat Blog
- Understanding rootless Podman's user namespace modes
- Make systemd better for Podman with Quadlet
- Build Kubernetes pods with Podman play kube
- 5 underused Podman features to try now
Community and Technical Analysis
- Podman 5 X Deep Dive — hrdtr.dev
- Podman vs Docker: Complete 2026 Comparison — Xurrent
- From Podman to Kubernetes — Better Stack
- Rootless network performance discussion — GitHub
- Podman Desktop 2025 Journey
- How does rootless Podman work? — Opensource.com
- Migrating from Docker — Podman Desktop Docs