Podman

Quick Reference

# Run container
podman run -d --name nginx -p 8080:80 nginx

# List containers
podman ps -a

# Stop/start/remove
podman stop nginx
podman start nginx
podman rm nginx

# Images
podman images
podman pull alpine
podman rmi alpine

# Pods
podman pod create --name mypod
podman run -d --pod mypod nginx

# Rootless (as regular user)
podman run --rm alpine echo "No root needed"

# Generate systemd service
podman generate systemd --name nginx --files

Understanding Podman

Podman vs Docker

Feature Docker Podman

Architecture

Client-server daemon

Daemonless (fork/exec)

Root requirement

Requires root daemon

Rootless by design

systemd integration

Separate daemon

Native systemd units

Pod support

Swarm/Compose

Native Kubernetes pods

Command compatibility

docker …​

podman …​ (drop-in)

Socket

/var/run/docker.sock

Per-user or root socket

Security

Root daemon = attack surface

No daemon = smaller surface

Architecture

┌─────────────────────────────────────────────────────────────┐
│                    User Space                                │
│  ┌─────────────┐  ┌─────────────┐  ┌─────────────────────┐  │
│  │   podman    │  │   buildah   │  │       skopeo        │  │
│  │  (CLI/API)  │  │ (build imgs)│  │   (copy images)     │  │
│  └─────────────┘  └─────────────┘  └─────────────────────┘  │
└─────────────────────────────────────────────────────────────┘
                              │
                              ▼
┌─────────────────────────────────────────────────────────────┐
│                       libpod                                 │
│              (container management library)                  │
└─────────────────────────────────────────────────────────────┘
                              │
                              ▼
┌─────────────────────────────────────────────────────────────┐
│                       conmon                                 │
│              (container monitor process)                     │
└─────────────────────────────────────────────────────────────┘
                              │
                              ▼
┌─────────────────────────────────────────────────────────────┐
│              OCI Runtime (runc/crun)                         │
└─────────────────────────────────────────────────────────────┘

Rootless Containers

Podman runs containers without root privileges:

# As regular user
podman run --rm alpine id
# uid=0(root) gid=0(root)  <- root inside container only

# User namespace mapping
podman unshare cat /proc/self/uid_map
#          0       1000       1
#          1     100000      65536

# Container root = your UID outside

Installation

Package Installation

# Arch Linux
pacman -S podman

# Fedora/RHEL
dnf install podman

# Debian/Ubuntu
apt install podman

# Verify
podman --version
podman info

Rootless Setup

# Check subuid/subgid (required for rootless)
grep $USER /etc/subuid
grep $USER /etc/subgid

# If missing, add:
sudo usermod --add-subuids 100000-165535 --add-subgids 100000-165535 $USER

# Enable lingering (keep user services after logout)
loginctl enable-linger $USER

# Verify rootless works
podman run --rm alpine echo "Rootless works!"

Container Operations

Run Containers

# Basic run
podman run alpine echo "Hello"

# Interactive
podman run -it alpine sh

# Detached with name
podman run -d --name mycontainer nginx

# Port mapping
podman run -d -p 8080:80 nginx
podman run -d -p 8080:80/tcp -p 8443:443/tcp nginx

# Environment variables
podman run -d -e MYSQL_ROOT_PASSWORD=secret mysql

# Volume mounts
podman run -d -v /host/path:/container/path nginx
podman run -d -v myvolume:/data nginx

# Resource limits
podman run -d --memory=512m --cpus=1 nginx

# Auto-remove on exit
podman run --rm alpine echo "Temporary"

# Run as specific user
podman run --user 1000:1000 alpine id

# Read-only filesystem
podman run --read-only alpine sh

List Containers

# Running containers
podman ps

# All containers (including stopped)
podman ps -a

# Just IDs
podman ps -q

# With size
podman ps -s

# Filter
podman ps --filter "status=running"
podman ps --filter "name=nginx"

# Format output
podman ps --format "{{.ID}} {{.Names}} {{.Status}}"

Container Lifecycle

# Start stopped container
podman start mycontainer

# Stop container
podman stop mycontainer
podman stop -t 30 mycontainer  # 30s timeout

# Restart
podman restart mycontainer

# Pause/unpause
podman pause mycontainer
podman unpause mycontainer

# Kill (SIGKILL)
podman kill mycontainer

# Remove
podman rm mycontainer
podman rm -f mycontainer  # Force (running)
podman rm -v mycontainer  # Remove with volumes

# Remove all stopped
podman container prune
podman rm $(podman ps -aq)

Inspect and Logs

# Container details
podman inspect mycontainer
podman inspect -f '{{.NetworkSettings.IPAddress}}' mycontainer

# Logs
podman logs mycontainer
podman logs -f mycontainer  # Follow
podman logs --tail 100 mycontainer
podman logs --since 1h mycontainer

# Stats
podman stats
podman stats mycontainer

# Top (processes)
podman top mycontainer

# Diff (filesystem changes)
podman diff mycontainer

Execute in Container

# Run command in running container
podman exec mycontainer ls /

# Interactive shell
podman exec -it mycontainer sh
podman exec -it mycontainer bash

# As specific user
podman exec -u root mycontainer whoami

# With environment variable
podman exec -e VAR=value mycontainer env

Copy Files

# Host to container
podman cp file.txt mycontainer:/path/

# Container to host
podman cp mycontainer:/path/file.txt ./

# Directory
podman cp mycontainer:/var/log/ ./logs/

Image Management

Pull Images

# Pull from default registry
podman pull nginx
podman pull nginx:alpine

# From specific registry
podman pull docker.io/library/nginx
podman pull quay.io/podman/hello

# All tags
podman pull --all-tags nginx

List Images

# All images
podman images

# Just IDs
podman images -q

# With digests
podman images --digests

# Filter
podman images --filter "dangling=true"

# Format
podman images --format "{{.Repository}}:{{.Tag}}"

Build Images

# Build from Dockerfile
podman build -t myimage .

# With specific Dockerfile
podman build -t myimage -f Dockerfile.prod .

# Build arguments
podman build --build-arg VERSION=1.0 -t myimage .

# No cache
podman build --no-cache -t myimage .

# Multi-stage target
podman build --target builder -t myimage .

Image Operations

# Tag image
podman tag nginx myregistry/nginx:v1

# Push to registry
podman push myregistry/nginx:v1

# Save to tar
podman save -o nginx.tar nginx

# Load from tar
podman load -i nginx.tar

# Export container filesystem
podman export mycontainer -o container.tar

# Import as image
podman import container.tar myimage

# Remove image
podman rmi nginx
podman rmi -f nginx

# Remove unused images
podman image prune
podman image prune -a  # All unused

Image History

# Show layers
podman history nginx

# Quiet (just IDs)
podman history -q nginx

# No truncation
podman history --no-trunc nginx

Pods

Understanding Pods

Pods group containers sharing: - Network namespace (same IP, ports) - IPC namespace - PID namespace (optional)

Pod Operations

# Create pod
podman pod create --name mypod

# Create with port mapping
podman pod create --name mypod -p 8080:80

# Run container in pod
podman run -d --pod mypod --name nginx nginx
podman run -d --pod mypod --name app myapp

# List pods
podman pod ls
podman pod ps

# Inspect pod
podman pod inspect mypod

# Pod stats
podman pod stats mypod

# Stop/start/restart pod (all containers)
podman pod stop mypod
podman pod start mypod
podman pod restart mypod

# Remove pod (and containers)
podman pod rm mypod
podman pod rm -f mypod

Pod from YAML

# Generate YAML from running pod
podman generate kube mypod > mypod.yaml

# Create pod from YAML
podman play kube mypod.yaml

# Stop and remove
podman play kube --down mypod.yaml

Example Pod YAML

apiVersion: v1
kind: Pod
metadata:
  name: webapp
spec:
  containers:
  - name: nginx
    image: nginx:alpine
    ports:
    - containerPort: 80
  - name: app
    image: myapp:latest
    env:
    - name: DATABASE_HOST
      value: "localhost"

Networking

Network Modes

# Bridge (default) - isolated network with NAT
podman run -d --network bridge nginx

# Host - use host network directly
podman run -d --network host nginx

# None - no networking
podman run -d --network none alpine

# Container - share another container's network
podman run -d --name web nginx
podman run -d --network container:web alpine

# Slirp4netns (rootless default)
podman run -d --network slirp4netns nginx

Custom Networks

# Create network
podman network create mynet

# With subnet
podman network create --subnet 10.10.0.0/24 mynet

# List networks
podman network ls

# Inspect
podman network inspect mynet

# Run container on network
podman run -d --network mynet --name web nginx
podman run -d --network mynet --name app myapp

# Containers can reach each other by name
podman exec app ping web

# Connect running container
podman network connect mynet existing-container

# Disconnect
podman network disconnect mynet existing-container

# Remove network
podman network rm mynet

Port Mapping

# Basic mapping
podman run -d -p 8080:80 nginx

# Specific host IP
podman run -d -p 127.0.0.1:8080:80 nginx

# UDP
podman run -d -p 53:53/udp dns-server

# Random host port
podman run -d -p 80 nginx
podman port <container>  # See assigned port

# Publish all exposed ports
podman run -d -P nginx

DNS and Hosts

# Custom DNS
podman run --dns 8.8.8.8 alpine cat /etc/resolv.conf

# DNS search domain
podman run --dns-search example.com alpine cat /etc/resolv.conf

# Add hosts entry
podman run --add-host myhost:192.168.1.100 alpine cat /etc/hosts

# Hostname
podman run --hostname mycontainer alpine hostname

Volumes and Storage

Volume Types

# Named volume (managed by Podman)
podman run -d -v myvolume:/data nginx

# Bind mount (host path)
podman run -d -v /host/path:/container/path nginx

# tmpfs (memory)
podman run -d --tmpfs /tmp nginx

# Read-only
podman run -d -v myvolume:/data:ro nginx

Volume Management

# Create volume
podman volume create myvolume

# List volumes
podman volume ls

# Inspect
podman volume inspect myvolume

# Remove
podman volume rm myvolume

# Prune unused
podman volume prune

Bind Mount Options

# Read-only
podman run -v /host:/container:ro nginx

# SELinux labels (RHEL/Fedora)
podman run -v /host:/container:Z nginx  # Private label
podman run -v /host:/container:z nginx  # Shared label

# Rootless with proper permissions
podman run -v /host:/container:Z --userns=keep-id nginx

systemd Integration

Generate systemd Units

# Generate unit file for container
podman generate systemd --name mycontainer > mycontainer.service

# With automatic creation
podman generate systemd --new --name mycontainer > mycontainer.service

# For pod
podman generate systemd --name mypod --files

# Options
podman generate systemd --name mycontainer \
    --restart-policy=always \
    --time 30 \
    --files

Install as User Service

# Create service directory
mkdir -p ~/.config/systemd/user/

# Generate and install
podman generate systemd --name nginx --files
mv container-nginx.service ~/.config/systemd/user/

# Reload and enable
systemctl --user daemon-reload
systemctl --user enable --now container-nginx.service

# Check status
systemctl --user status container-nginx.service

# View logs
journalctl --user -u container-nginx.service

Install as Root Service

# Generate
sudo podman generate systemd --name nginx --files

# Move to system directory
sudo mv container-nginx.service /etc/systemd/system/

# Enable
sudo systemctl daemon-reload
sudo systemctl enable --now container-nginx.service

Auto-Update with systemd

# Create container with auto-update label
podman run -d --label "io.containers.autoupdate=registry" \
    --name nginx nginx:latest

# Generate service
podman generate systemd --new --name nginx > ~/.config/systemd/user/nginx.service

# Enable auto-update timer
systemctl --user enable --now podman-auto-update.timer

# Check for updates manually
podman auto-update

# Dry run
podman auto-update --dry-run

Podman Compose

Using docker-compose Files

# Install podman-compose
pip install podman-compose

# Or use podman's built-in compose
podman compose up -d

# With docker-compose.yml
podman-compose up -d
podman-compose down
podman-compose logs

Example docker-compose.yml

version: '3'
services:
  web:
    image: nginx:alpine
    ports:
      - "8080:80"
    volumes:
      - ./html:/usr/share/nginx/html:Z
  db:
    image: postgres:alpine
    environment:
      POSTGRES_PASSWORD: secret
    volumes:
      - pgdata:/var/lib/postgresql/data

volumes:
  pgdata:

Registry Configuration

Configure Registries

/etc/containers/registries.conf (system) or ~/.config/containers/registries.conf (user)
[registries.search]
registries = ['docker.io', 'quay.io', 'registry.fedoraproject.org']

[registries.insecure]
registries = ['myregistry.local:5000']

[registries.block]
registries = ['docker.io/badimage']

Login to Registry

# Login
podman login docker.io
podman login quay.io
podman login myregistry.local:5000

# With credentials
podman login -u user -p password registry.example.com

# Check login
podman login --get-login docker.io

# Logout
podman logout docker.io
podman logout --all

Security

Run as Non-Root

# Run as specific user
podman run --user 1000:1000 alpine id

# Keep host UID (rootless)
podman run --userns=keep-id alpine id

# No new privileges
podman run --security-opt no-new-privileges alpine

Capabilities

# Drop all capabilities
podman run --cap-drop=all alpine

# Add specific capability
podman run --cap-add=NET_ADMIN alpine

# Check capabilities
podman run --rm alpine cat /proc/1/status | grep Cap

SELinux

# Run with SELinux disabled (not recommended)
podman run --security-opt label=disable alpine

# Custom SELinux type
podman run --security-opt label=type:container_runtime_t alpine

# Volume labels
podman run -v /host:/container:Z alpine  # Private
podman run -v /host:/container:z alpine  # Shared

Seccomp

# Custom seccomp profile
podman run --security-opt seccomp=/path/to/profile.json alpine

# Disable seccomp (not recommended)
podman run --security-opt seccomp=unconfined alpine

Read-Only Containers

# Read-only root filesystem
podman run --read-only alpine

# With tmpfs for writable locations
podman run --read-only --tmpfs /tmp --tmpfs /run alpine

Troubleshooting

Common Issues

Permission Denied (Rootless)

# Check subuid/subgid
grep $USER /etc/subuid /etc/subgid

# Reset storage
podman system reset

# Check user namespace
podman unshare cat /proc/self/uid_map

Port Binding Failed

# Rootless cannot bind < 1024 by default
# Option 1: Use higher port
podman run -p 8080:80 nginx

# Option 2: Allow low ports (sysctl)
sudo sysctl net.ipv4.ip_unprivileged_port_start=80

Storage Issues

# Check storage driver
podman info | grep -i storage

# Clean up
podman system prune -a
podman volume prune

# Reset completely
podman system reset

Networking Issues

# Check network
podman network inspect podman

# Test connectivity from container
podman run --rm alpine ping -c1 8.8.8.8

# Check firewall
sudo firewall-cmd --list-all

# For rootless, check slirp4netns
podman info | grep -i network

Debug Commands

# System info
podman info

# Container details
podman inspect <container>

# Events
podman events

# System health
podman system df

# Check storage
podman system info --format '{{.Store.GraphRoot}}'

Quick Command Reference

# Containers
podman run -d --name web -p 8080:80 nginx
podman ps -a
podman stop/start/restart web
podman rm web
podman exec -it web sh
podman logs -f web

# Images
podman images
podman pull nginx
podman build -t myimage .
podman rmi nginx

# Pods
podman pod create --name mypod -p 8080:80
podman run -d --pod mypod nginx
podman pod ls/stop/start/rm mypod

# Volumes
podman volume create mydata
podman run -v mydata:/data nginx
podman volume ls/rm mydata

# Networks
podman network create mynet
podman run --network mynet nginx
podman network ls/rm mynet

# systemd
podman generate systemd --name web --files
systemctl --user enable container-web.service

# Cleanup
podman system prune -a
podman volume prune
podman system reset

See Also