Skip to content
Published on

Bringing Kubernetes to Your Local Machine: 5 Game-Changing Innovations from Podman

Authors
  • Name
    Twitter

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 limitationsPodman, 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:

InnovationKey ThemeProblem Solved
Innovation 1Daemonless ArchitectureEliminates single point of failure (SPOF)
Innovation 2Rootless SecurityEradicates container escape risk
Innovation 3play kubeResolves dev/ops language duality
Innovation 4Pod-Native ManagementKubernetes-native workflow
Innovation 5Pasta NetworkingRemoves 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:

  1. The user executes podman run
  2. The Podman CLI directly fork/execs the OCI runtime (default: crun, or runc)
  3. conmon (Container Monitor) is created to manage the container process's stdout/stderr and exit code
  4. The Podman CLI process finishes its role and exits
  5. 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:

CategoryLegacy (generate systemd)Quadlet
File formatAuto-generated long ExecStartDeclarative .container files
MaintenanceRegeneration needed on image updateDeclarative edit + daemon-reload
ReadabilityAll options on a single command lineStructured 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:

CVEVulnerabilityImpact
CVE-2019-5736runc vulnerabilityOverwrite host runc binary from container
CVE-2020-15257containerd Shim APIAccess host network namespace
CVE-2022-0185Kernel heap overflowGain host root from unprivileged container
CVE-2024-21626runc file descriptor leakAccess 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:

ModeDescriptionUse Case
autoAutomatic UID/GID range allocation (default)General rootless containers
keep-idMaps host user UID identically inside the containerPreserving bind mount file permissions
nomapDoes not map host user UID to the containerMaximum security isolation
hostDisables User NamespaceLegacy 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:

TypeBehaviorUse Case
once (default)Runs once at Pod start then removedDB migration, secret initialization
alwaysRuns 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:

  1. Infra container: Same role as Kubernetes pause container. Owns the network namespace, shared by all containers in the Pod
  2. localhost communication: Containers within a Pod communicate via localhost (127.0.0.1)
  3. Namespace sharing: Network, IPC, and UTS namespaces shared by default
  4. 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:

  1. Host network configuration copying: Copies the host's network config directly to the container namespace, making NAT itself unnecessary
  2. L2-L4 socket mapping: Directly maps Layer-2 TAP interface packets to Layer-4 sockets (TCP/UDP/ICMP)
  3. splice(2) system call: Uses the kernel's splice(2) system call for zero-copy data transfer on local connections
  4. recvmmsg/sendmmsg optimization: Processes multiple packets in a single system call, reducing syscall overhead
  5. Tap Bypass Path: Packets destined for local addresses skip L2 conversion and transfer directly between L4 sockets

6.3 Performance Comparison: slirp4netns vs Pasta

Metricslirp4netnsPastaImprovement
Container start time~1.2s~0.8s33% faster
Memory usage~100MB~85MB15% less
TCP throughput (1 conn)Baseline+15-30%Significant
TCP latency (p50)Baseline-20-40%Major improvement
UDP throughputBaseline+10-20%Improved
Parallel connections (8 or fewer)BaselineAdvantagePasta wins
Parallel connections (more than 8)AdvantageBaselineslirp4netns wins*
CPU overhead (idle)HighLowSignificant

*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

ScenarioRecommended Network ModeReason
General rootless developmentpasta (default)Best performance/compatibility balance
Legacy compatibility neededslirp4netnsStable on older kernels
Root containersbridge (CNI/netavark)Traditional bridge in root mode
Direct host network accesshostWhen network isolation not needed
Inter-container communication in Podpod shared namespacelocalhost 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:

FeatureDescription
Container managementCreate, start, stop, delete, view logs, terminal access
Image managementBuild, pull, push, search, prune (unused image cleanup)
Pod managementPod creation, Kubernetes YAML deployment, Pod log search
Volumes/NetworksVolume create/delete, network visibility and management
Kubernetes integrationKind, Minikube, OpenShift cluster integration
Extension ecosystemBootC, Headlamp, AI Lab extensions
Compose supportDocker Compose file compatible execution
Dashboard customizationWidget 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 CommandPodman CommandDifference
docker runpodman runIdentical
docker buildpodman buildIdentical
docker pullpodman pullIdentical
docker pushpodman pushIdentical
docker pspodman psIdentical
docker execpodman execIdentical
docker logspodman logsIdentical
docker imagespodman imagesIdentical
docker composepodman composeSeparate install or podman-compose
docker swarmNot supportedUse Kubernetes instead
N/Apodman podPodman-exclusive feature
N/Apodman kube playPodman-exclusive feature
N/Apodman kube generatePodman-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

AreaDockerPodmanMitigation
Daemon stateAlways runningNo daemonConfigure auto-start via systemd
Docker Socket/var/run/docker.sockPodman Socket must be enabledEnable podman.socket service
Docker SwarmSupportedNot supportedMigrate to Kubernetes
Image storage/var/lib/docker~/.local/share/containers (rootless)Existing images must be re-pulled
Network driverbridge (dockerd managed)Netavark + Pasta (rootless)Compatible, verify configuration
Build cacheDocker BuildKitPodman Buildah built-inCompatible

9. Podman vs Docker: Comprehensive Comparison

Comparison ItemDockerPodman
ArchitectureClient-server (dockerd daemon)Daemonless (Fork-Exec)
Default execution moderoot (rootless is experimental)rootless (default)
Pod supportNone (Compose as substitute)Native Pod support
Kubernetes YAML executionRequires separate toolspodman kube play built-in
systemd integrationLimitedNative via Quadlet
Container start time~1.2s~0.8s
Idle memory~100MB (includes daemon)~85MB
Idle CPUDaemon always running0% (no daemon)
Kernel Capabilities14 (default)11 (default)
OCI complianceFully compliantFully compliant
Image compatibilityDocker Hub defaultDocker Hub + all OCI registries
Build toolBuildKitBuildah built-in
Rootless networkingslirp4netnsPasta (default)
Docker ComposeNativeCompatible (podman compose)
Docker SwarmSupportedNot supported (Kubernetes recommended)
GUI toolDocker Desktop (paid*)Podman Desktop (free/open-source)
LicenseApache 2.0 (Engine) / Paid (Desktop)Apache 2.0 (entirely free)
Multi-platform buildbuildxpodman farm build
Secret managementDocker Secrets (Swarm)podman secret
TLS/mTLS remote accessSupportedEnhanced in 5.x
OCI Artifact managementLimitedpodman 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

Red Hat Blogs and Technical Documentation

Community and Technical Analysis

Security and Compliance