Available for day contractsFrom 21st September I have availability for day and half day contracts. Please contact for more information.

Contact →
mikepreston.org

Podman

A daemonless, rootless container engine compatible with OCI standards and Docker CLI syntax.

Podman

A daemonless, rootless container engine compatible with OCI standards and Docker CLI syntax.

Overview

Podman is a container engine that provides a Docker-compatible command-line interface without requiring a daemon process. It excels at running containers as non-root users, integrating with systemd for service management, and managing pods (groups of containers sharing resources). Podman is particularly suited for security-conscious environments and systems where systemd integration is desirable.

StoragePodman ArchitecturePod StructurePodInfra ContainerContainer 1Container 2Shared NamespacesPodman CLIContainer Runtimeconmonrunc/crunImage StoreContainer Store/var/lib/containersStoragePodman ArchitecturePod StructurePodInfra ContainerContainer 1Container 2Shared NamespacesPodman CLIContainer Runtimeconmonrunc/crunImage StoreContainer Store/var/lib/containers

Version note

Podman 6.0 (June 2026) removed several things that older material still assumes. Everything here works on 5.4+ unless flagged; check these before upgrading a 5.x host:

Removed in 6.0 Replacement
CNI networking netavark (default since 4.0)
slirp4netns pasta (default since 5.0)
cgroups v1 cgroups v2
iptables nftables
BoltDB state SQLite (auto-migrated on first 6.x start)
Intel Mac and Windows 10 hosts —
podman version
podman info --format '{{.Host.CgroupsVersion}} {{.Host.NetworkBackend}} {{.Host.DatabaseBackend}}'
# v2 netavark sqlite

Rootless Containers

Key Concepts

Rootless containers run entirely in user space without requiring root privileges, providing enhanced security through user namespace isolation.

Advantages:

  • Security: Compromised containers cannot escalate to root on the host
  • Multi-tenancy: Multiple users can run containers independently
  • No daemon: Each user manages their own container storage
  • Audit trail: Container actions tied to specific user accounts

Limitations:

  • Cannot bind to privileged ports (< 1024) without configuration
  • Some volume mounts may have permission constraints
  • Network performance slightly reduced by user-space networking (pasta; slirp4netns was the old default and is gone in 6.0)

Common Commands/Patterns

# Check rootless prerequisites
podman info --format '{{.Host.Security.Rootless}}'

# View user namespace configuration
cat /etc/subuid
cat /etc/subgid

# Add subuid/subgid ranges for a user (as root)
usermod --add-subuids 100000-165535 --add-subgids 100000-165535 username

# Reset rootless storage (if corrupted)
podman system reset

# Run container as specific user inside container
podman run --user 1000:1000 nginx

# Check current user's container storage location
podman info --format '{{.Store.GraphRoot}}'

Examples

# Set up rootless environment for new user
sudo usermod --add-subuids 200000-265535 --add-subgids 200000-265535 newuser

# Allow binding to port 80 without root (requires sysctl)
sudo sysctl net.ipv4.ip_unprivileged_port_start=80

# Run rootless container with port mapping
podman run -d -p 8080:80 --name web nginx:alpine

# Verify rootless operation
podman info | grep -i rootless

Docker Compatibility

Key Concepts

Podman provides a Docker-compatible CLI, allowing most Docker commands to work unchanged. The podman-docker package installs a docker alias pointing to Podman.

Docker Commandspodman-dockerPodman EngineContainersDocker Composepodman-composeDocker APIpodman systemserviceDocker Commandspodman-dockerPodman EngineContainersDocker Composepodman-composeDocker APIpodman systemservice

Common Commands/Patterns

# Install docker compatibility (Fedora/RHEL)
sudo dnf install podman-docker

# Enable Docker-compatible API socket
podman system service --time=0 unix:///tmp/podman.sock &

# Set DOCKER_HOST for compatibility
export DOCKER_HOST=unix:///tmp/podman.sock

# Use docker-compose with Podman socket
DOCKER_HOST=unix:///tmp/podman.sock docker-compose up

# Equivalent commands (all work identically)
docker run nginx          # With podman-docker installed
podman run nginx          # Native Podman

# Build images (identical syntax)
podman build -t myapp:latest .
docker build -t myapp:latest .

# Pull from Docker Hub
podman pull docker.io/library/nginx:latest

Examples

# Run Podman API as systemd user service
systemctl --user enable --now podman.socket

# Verify socket location
ls -la /run/user/$(id -u)/podman/podman.sock

# Use with Docker clients
export DOCKER_HOST=unix:///run/user/$(id -u)/podman/podman.sock
docker ps
docker images

Pod Management

Key Concepts

Pods are groups of containers sharing kernel namespaces — ipc, net and uts by default, matching Kubernetes; pid and cgroup are opt-in. This mirrors Kubernetes pod semantics, making local development and testing easier.

Three rules follow from the shared network namespace, and between them account for most first-day confusion:

  • Ports are published on the pod, not on its containers. --publish on a podman run --pod is an error; put it on podman pod create.
  • Containers reach each other over localhost, so two containers in one pod cannot both bind the same port.
  • An infra container holds the namespaces open so the pod survives individual containers restarting. It is created by default (--infra=false to skip it, in which case nothing is shared unless --share says otherwise).
Pod: webappInfra ContainerpauseNetwork NSIPC NSnginx:80php-fpm:9000redis:6379Host Port 8080Pod: webappInfra ContainerpauseNetwork NSIPC NSnginx:80php-fpm:9000redis:6379Host Port 8080

Common Commands/Patterns

# Create a new pod
podman pod create --name mypod

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

# Create pod with resource limits shared across all its containers
podman pod create --name limited \
  --cpus 2 \
  --memory 1g

# Choose which namespaces are shared (default: ipc,net,uts)
podman pod create --name shared --share ipc,net,uts,pid
podman pod create --name shared --share +pid      # append to the defaults
podman pod create --name isolated --share "" --infra=true

# Stop the pod when its last container exits (default is 'continue')
podman pod create --name job --exit-policy stop

# Name the infra container explicitly
podman pod create --name webapp --infra-name webapp-infra

# List all pods
podman pod ls

# Inspect pod details
podman pod inspect mypod

# View pod processes and live resource usage
podman pod top mypod
podman pod stats mypod

# Start/stop/restart pods
podman pod start mypod
podman pod stop mypod
podman pod restart mypod

# Remove pod (and all containers)
podman pod rm mypod

# Force remove running pod
podman pod rm -f mypod

# Remove all pods
podman pod rm --all

# View pod logs (all containers, interleaved)
podman pod logs mypod
podman pod logs -f --names mypod

# Which container holds the namespaces
podman pod inspect mypod --format '{{.InfraContainerID}}'

Examples

# Create a complete web application pod
podman pod create --name webapp -p 8080:80

# Add nginx container to pod
podman run -d --pod webapp --name nginx \
  -v ./html:/usr/share/nginx/html:ro \
  nginx:alpine

# Add PHP-FPM container to same pod
podman run -d --pod webapp --name php \
  -v ./php:/var/www/html:ro \
  php:8-fpm-alpine

# Add Redis container
podman run -d --pod webapp --name redis \
  redis:alpine

# Containers can communicate via localhost
podman exec php ping -c 1 localhost  # Reaches nginx and redis

# Ports belong to the pod: this fails
podman run -d --pod webapp -p 9000:9000 nginx:alpine
# Error: invalid config provided: published or exposed ports must be defined
# when the pod is created

# Generate Kubernetes YAML from a pod
podman kube generate webapp > webapp.yaml

# Create pod from Kubernetes YAML
podman kube play webapp.yaml

# Tear down a Kubernetes-defined pod
podman kube down webapp.yaml
podman kube play --down webapp.yaml    # equivalent

The older podman generate kube / podman play kube spellings still work as hidden aliases, but podman kube generate / podman kube play are the current forms and the ones the documentation uses.

For running pods as systemd services — which is how you would actually deploy one — see the Podman Quadlets and systemd cheatsheet.

Systemd Integration

Key Concepts

Podman has no daemon, so systemd is its supervisor: restart policies, dependency ordering, boot-time start, and logging all come from systemd rather than from Podman itself. The supported way to wire that up is Quadlet — a systemd generator that turns short declarative files (.container, .pod, .network, .volume, .kube, .build, .image) into real service units at boot and on every daemon-reload.

podman generate systemd is deprecated (since 4.7; bug fixes only, no new features). Use it to read what an existing deployment does, not to build a new one.

daemon-reloadExecStartreadiness + healthjournaldwebapp.containerQuadlet generatorwebapp.servicesystemdpodman runContainerjournalctl -u webappdaemon-reloadExecStartreadiness + healthjournaldwebapp.containerQuadlet generatorwebapp.servicesystemdpodman runContainerjournalctl -u webapp

Common Commands/Patterns

# Quadlet files live here (rootless)
mkdir -p ~/.config/containers/systemd

# ...and here (rootful)
sudo mkdir -p /etc/containers/systemd

# The generator runs on reload; there is no install step
systemctl --user daemon-reload
systemctl --user start webapp

# Generated units are transient: `systemctl enable` will NOT work.
# Boot-time start comes from the [Install] section in the Quadlet file.

# Inspect what was generated
systemctl --user cat webapp.service
/usr/lib/systemd/system-generators/podman-system-generator --user --dryrun

# Logs
journalctl --user -u webapp.service -f
sudo journalctl -u webapp.service -f      # rootful

# Survive logout and start at boot, rootless
loginctl enable-linger $USER

# Podman's own shipped units
systemctl --user enable --now podman-auto-update.timer
systemctl --user enable --now podman.socket        # Docker-compatible API

Examples

# A container as a user service
cat > ~/.config/containers/systemd/webapp.container << 'EOF'
[Unit]
Description=Web application

[Container]
Image=docker.io/library/nginx:1.29-alpine
PublishPort=8080:80
Volume=webapp-data.volume:/usr/share/nginx/html:ro,z
HealthCmd=curl -fsS http://localhost/
HealthInterval=30s
HealthOnFailure=kill
AutoUpdate=registry

[Service]
Restart=always

[Install]
WantedBy=default.target
EOF

# The volume it depends on
cat > ~/.config/containers/systemd/webapp-data.volume << 'EOF'
[Volume]
VolumeName=webapp-data
EOF

systemctl --user daemon-reload
systemctl --user start webapp
systemctl --user status webapp

Note what you did not have to write: the podman run line, the ExecStop, the volume ordering, or the Type=notify plumbing. Quadlet generates all of it, and the container is named systemd-webapp unless you set ContainerName=.

The full treatment — multi-container pods, .network and .build units, sdnotify readiness, healthcheck-gated start-up, timers for scheduled jobs, auto-updates with rollback, and migrating off podman generate systemd — is in the Podman Quadlets and systemd cheatsheet.

Health Checks

Key Concepts

A healthcheck is a command Podman runs inside the container on a schedule. With no daemon to poll from, Podman schedules each check as a transient systemd timer created with systemd-run — so on a host without systemd (a build container, a minimal chroot) checks are defined but never fire on their own.

A container has three health states. It begins starting, and the first successful check promotes it to healthy — immediately, even mid start-period. What the start period buys is a grace window in which failures are ignored: they leave the status at starting and do not count towards --health-retries. Only once the window has passed do --health-retries consecutive failures mark it unhealthy.

container startedcheck passesfailure inside start periodretries exhaustedcheck passes againon-failure actionstartinghealthyunhealthycontainer startedcheck passesfailure inside start periodretries exhaustedcheck passes againon-failure actionstartinghealthyunhealthy

Common Commands/Patterns

# Define a check at run time
podman run -d --name api \
  --health-cmd 'curl -fsS http://localhost:9000/healthz' \
  --health-interval 30s \
  --health-timeout 5s \
  --health-retries 3 \
  --health-start-period 20s \
  --health-on-failure kill \
  localhost/api:latest

# Run the check now: exit 0 healthy, 1 unhealthy, 125 error
podman healthcheck run api

# Read the state
podman inspect api --format '{{.State.Health.Status}} {{.State.Health.FailingStreak}}'
podman inspect api --format '{{json .State.Health.Log}}' | jq '.[-1]'

# The ps STATUS column carries it too
podman ps --format '{{.Names}}\t{{.Status}}'      # api   Up 3 minutes (healthy)
podman ps --filter health=unhealthy

# Watch transitions
podman events --filter event=health_status

# A startup check gates the regular one — better than a huge start period,
# because it can restart a container that is genuinely wedged
podman run -d --name db \
  --health-cmd 'pg_isready -U postgres' \
  --health-interval 30s \
  --health-startup-cmd 'pg_isready -U postgres' \
  --health-startup-interval 2s \
  --health-startup-retries 60 \
  docker.io/library/postgres:17-alpine
Flag Default Meaning
--health-cmd from image Runs inside the container; a bare string becomes CMD-SHELL
--health-interval 30s disable skips timer creation entirely
--health-timeout 30s Per-check deadline
--health-retries 3 Consecutive failures before unhealthy
--health-start-period 0s Grace window; failures inside it do not count
--health-on-failure none none, kill, restart, stop

Examples

# Under systemd, prefer 'kill' and let the unit's Restart= policy recover.
# 'restart' has Podman restart the container behind systemd's back, which
# gives you two restart loops fighting each other with no shared backoff.
podman run -d --name api --health-on-failure kill ... localhost/api:latest

# Gate systemd readiness on the healthcheck rather than on process start,
# so a dependent unit's After= actually means "after it works"
podman run -d --name db \
  --sdnotify=healthy \
  --health-cmd 'pg_isready -U postgres' \
  --health-interval 10s \
  docker.io/library/postgres:17-alpine

# Healthcheck timers are per-container transient units
systemctl --user list-timers --all | grep "$(podman inspect -f '{{.Id}}' api | cut -c1-12)"

In a Quadlet file every flag has a key — HealthCmd=, HealthInterval=, HealthOnFailure=, and so on — plus Notify=healthy for the readiness gating above. See the Podman Quadlets and systemd cheatsheet.

Image and Container Management

Key Concepts

While Podman commands mirror Docker, there are key differences in storage locations, image handling, and default behaviours.

Aspect Docker Podman
Storage location /var/lib/docker /var/lib/containers (root) or ~/.local/share/containers (rootless)
Default registry docker.io Configurable via registries.conf
Image format Docker format OCI format (compatible)
Build tool BuildKit Buildah (integrated)

Common Commands/Patterns

# Pull image (specify full registry path recommended)
podman pull docker.io/library/nginx:latest
podman pull quay.io/podman/hello

# List images
podman images
podman image ls

# Inspect image
podman image inspect nginx:latest

# Build image
podman build -t myapp:latest .

# Build with specific Containerfile
podman build -f Containerfile.prod -t myapp:prod .

# Tag image
podman tag myapp:latest registry.example.com/myapp:v1.0

# Push to registry
podman push registry.example.com/myapp:v1.0

# Save/load images
podman save -o nginx.tar nginx:latest
podman load -i nginx.tar

# Remove images
podman rmi nginx:latest
podman image prune          # Remove dangling images
podman image prune -a       # Remove all unused images

# Container lifecycle
podman run -d --name web nginx:alpine
podman ps                    # List running
podman ps -a                 # List all
podman start web
podman stop web
podman restart web
podman rm web
podman rm -f web            # Force remove running

# Execute in running container
podman exec -it web /bin/sh

# View logs
podman logs web
podman logs -f web          # Follow
podman logs --tail 100 web  # Last 100 lines

# Inspect container
podman inspect web

# View resource usage
podman stats
podman top web

# Prune all unused data
podman system prune -a

Examples

# Configure default registries
# Edit /etc/containers/registries.conf or ~/.config/containers/registries.conf
cat > ~/.config/containers/registries.conf << 'EOF'
unqualified-search-registries = ["docker.io", "quay.io"]

[[registry]]
prefix = "docker.io"
location = "docker.io"
EOF

# Build multi-architecture image
podman build --platform linux/amd64,linux/arm64 \
  --manifest myapp:latest .

# Push manifest list
podman manifest push myapp:latest docker://registry.example.com/myapp:latest

# Copy image between storage
podman image scp user1@host1::myimage user2@host2::

# Export/import container filesystem
podman export web > web-filesystem.tar
podman import web-filesystem.tar myimage:imported

# Commit container changes to new image
podman commit web myimage:modified

Volume and Network Management

Key Concepts

Podman supports named volumes, bind mounts, and tmpfs mounts. Rootless networking uses pasta for user-space networking (the default since 5.0, and the only option from 6.0); rootful networking uses netavark (the default since 4.0, and the only option from 6.0).

Network ModesbridgeDefault isolatednetworkhostShare host networknoneNo networkingcontainer:idShare with anothercontainerpastaRootless user-spacenetworkingVolume TypesNamed Volume/var/lib/containers/storage/volumesBind MountHost DirectorytmpfsMemoryNetwork ModesbridgeDefault isolatednetworkhostShare host networknoneNo networkingcontainer:idShare with anothercontainerpastaRootless user-spacenetworkingVolume TypesNamed Volume/var/lib/containers/storage/volumesBind MountHost DirectorytmpfsMemory

Common Commands/Patterns

# Volume management
podman volume create mydata
podman volume ls
podman volume inspect mydata
podman volume rm mydata
podman volume prune

# Run with named volume
podman run -d --name db \
  -v pgdata:/var/lib/postgresql/data \
  postgres:15

# Run with bind mount
podman run -d --name web \
  -v /host/path:/container/path:ro \
  nginx:alpine

# SELinux volume labels
podman run -v ./data:/data:z nginx    # Shared label
podman run -v ./data:/data:Z nginx    # Private label

# tmpfs mount
podman run --tmpfs /tmp:size=100m nginx

# Network management
podman network create mynet
podman network ls
podman network inspect mynet
podman network rm mynet
podman network prune

# Connect container to network
podman run -d --name web --network mynet nginx
podman network connect mynet existing-container
podman network disconnect mynet existing-container

# Create network with specific subnet
podman network create --subnet 10.89.0.0/24 mynet

# DNS resolution between containers
# Custom networks have container-name DNS enabled by default (use --disable-dns to turn it off)
podman network create mynet
podman run -d --name db --network mynet postgres
podman run -d --name app --network mynet myapp
# App can reach database at hostname 'db'

# Run with host networking
podman run --network host nginx

# Run without networking
podman run --network none alpine

Examples

# Create application with persistent storage and custom network
podman network create app-network --subnet 172.20.0.0/16

podman volume create app-data
podman volume create db-data

# Database container
podman run -d --name postgres \
  --network app-network \
  -v db-data:/var/lib/postgresql/data \
  -e POSTGRES_PASSWORD=secret \
  postgres:15

# Application container
podman run -d --name myapp \
  --network app-network \
  -v app-data:/app/data \
  -p 8080:8080 \
  -e DATABASE_URL=postgresql://postgres:secret@postgres:5432/app \
  myapp:latest

# Inspect volume usage
podman system df -v

# Backup volume
podman run --rm \
  -v db-data:/data:ro \
  -v ./backup:/backup \
  alpine tar czf /backup/db-backup.tar.gz -C /data .

# Restore volume
podman run --rm \
  -v db-data:/data \
  -v ./backup:/backup:ro \
  alpine tar xzf /backup/db-backup.tar.gz -C /data

Podman Compose

Key Concepts

Podman runs Compose files three ways, in descending order of preference:

  • podman compose (4.7+): a thin wrapper that finds an external provider (docker-compose first, then podman-compose), starts the Podman socket, and points the provider at it. This is the current recommendation — nothing to configure.
  • docker-compose against the Podman socket: the same thing done by hand, with DOCKER_HOST set yourself.
  • podman-compose: an independent Python reimplementation. Useful where docker-compose cannot be installed, but its coverage of the Compose specification lags.

For anything long-lived, Quadlets are the better fit on a Podman host. Compose has its own restart policies, healthchecks, and depends_on conditions (including service_healthy, shown below), so the difference is not features — it is who supervises. Compose needs its own process or an outer unit to survive a reboot, and its ordering stops at the project boundary; Quadlet units are ordinary systemd services, so they start at boot, restart under systemd's backoff, log to journald, and can be ordered against anything else on the host. Compose is for development loops; Quadlets are for deployment.

docker-compose.ymlCompose Toolpodman-composedocker-compose +Podman socketPodmanPodman API SocketContainersNetworksVolumesdocker-compose.ymlCompose Toolpodman-composedocker-compose +Podman socketPodmanPodman API SocketContainersNetworksVolumes

Common Commands/Patterns

# Preferred: let Podman find and wire up a provider
podman compose up -d
podman compose ps
podman compose logs -f api
podman compose down

# Which provider did it pick?
podman compose --help | head -3

# Pin a provider explicitly
export PODMAN_COMPOSE_PROVIDER=/usr/bin/podman-compose
# or set compose_providers in the [engine] table of containers.conf

# Install podman-compose if you want the native implementation
sudo dnf install podman-compose        # Fedora/RHEL
uv tool install podman-compose         # anywhere with uv

# Basic operations (identical under any of the three routes)
podman compose up -d
podman compose down
podman compose down -v            # ...and remove volumes
podman compose ps
podman compose logs -f api
podman compose exec db /bin/sh
podman compose up -d --build
podman compose up -d --scale web=3
podman compose pull
podman compose config

# Doing it by hand with docker-compose against the Podman socket
systemctl --user start podman.socket
export DOCKER_HOST=unix:///run/user/$(id -u)/podman/podman.sock
docker-compose up -d

# Silence the "executing external compose provider" warning
export PODMAN_COMPOSE_WARNING_LOGS=false

Examples

# Example docker-compose.yml
cat > compose.yaml << 'EOF'
services:
  web:
    image: nginx:alpine
    ports:
      - "8080:80"
    volumes:
      - ./html:/usr/share/nginx/html:ro
    depends_on:
      - api

  api:
    build: ./api
    environment:
      - DATABASE_URL=postgresql://postgres:secret@db:5432/app
    depends_on:
      db:
        condition: service_healthy

  db:
    image: postgres:17-alpine
    environment:
      - POSTGRES_PASSWORD=secret
      - POSTGRES_DB=app
    volumes:
      - pgdata:/var/lib/postgresql/data
    healthcheck:
      test: ["CMD-SHELL", "pg_isready -U postgres"]
      interval: 10s
      retries: 5

volumes:
  pgdata:
EOF

# Start all services
podman compose up -d

# View status
podman compose ps

# Follow logs for a specific service
podman compose logs -f api

# Execute a command in a service
podman compose exec db psql -U postgres -d app

# Rebuild and restart a single service
podman compose up -d --build api

# Stop and remove everything including volumes
podman compose down -v

# podman-compose-specific: put every service in one pod so they share
# a network namespace and reach each other on localhost
podman-compose --in-pod=true up -d

The version: key at the top of the file is obsolete in the Compose specification and warns on modern providers — drop it. Note also that Compose's depends_on only waits for the container to start, not to be ready; the service_healthy condition (which does wait) needs a healthcheck defined on the dependency.

Quick Reference

Operation Command
Run container podman run -d --name web -p 8080:80 nginx
List containers podman ps -a
Stop container podman stop web
Remove container podman rm web
View logs podman logs -f web
Execute command podman exec -it web /bin/sh
Build image podman build -t myapp .
List images podman images
Remove image podman rmi myapp
Create pod podman pod create --name mypod -p 8080:80
Run in pod podman run -d --pod mypod nginx
List pods podman pod ls
Create volume podman volume create mydata
Create network podman network create mynet
Health status podman inspect web --format '{{.State.Health.Status}}'
Run healthcheck now podman healthcheck run web
Run under systemd Write ~/.config/containers/systemd/web.container, then systemctl --user daemon-reload
Preview Quadlet output /usr/lib/systemd/system-generators/podman-system-generator --user --dryrun
Pod to Kubernetes YAML podman kube generate mypod > pod.yaml
Kubernetes YAML to pod podman kube play pod.yaml
System cleanup podman system prune -a
Check disk usage podman system df
Start compose podman compose up -d
Stop compose podman compose down

Common Issues and Solutions

Issue Cause Solution
ERRO[0000] cannot find UID/GID Missing subuid/subgid entries Run sudo usermod --add-subuids 100000-165535 --add-subgids 100000-165535 $USER
Permission denied on volume SELinux blocking access Add :z or :Z suffix to volume mount
Cannot bind to port < 1024 Rootless limitation Use higher port or set sysctl net.ipv4.ip_unprivileged_port_start=80
Image pull fails Registry not configured Add registry to registries.conf or use full path docker.io/library/nginx
Error: statfs: no such file or directory Corrupted storage Run podman system reset (WARNING: removes all data)
Slow rootless networking Legacy slirp4netns in use pasta is the default from 5.0 and the only option from 6.0; drop any --network slirp4netns pin
Container DNS not working Using the default network or DNS disabled Create a custom network (podman network create mynet) — container-name DNS is on by default there; the default podman network has no name resolution
Quadlet unit "not found" after daemon-reload Generation failed on an unsupported or misspelled key /usr/lib/systemd/system-generators/podman-system-generator --user --dryrun prints the error
systemctl enable fails on a Quadlet unit Generated units are transient Put [Install] WantedBy=default.target in the Quadlet file instead
User services stop at logout Lingering not enabled loginctl enable-linger $USER
Healthcheck never runs by itself Checks are driven by transient systemd timers Needs a running systemd; verify with systemctl --user list-timers --all
Container in a pod rejects -p Ports belong to the pod Publish on podman pod create instead
Error: OCI runtime error cgroup v2 issues Ensure cgroup v2 is enabled (mandatory from 6.0); on 5.x, --cgroup-manager=cgroupfs is a stopgap
Compose networking issues Default network mode Try podman-compose --in-pod=true up for shared networking
Volume mount shows empty Wrong path or SELinux Verify path exists and check ls -laZ for SELinux context

Debugging Commands

# Check Podman system info
podman info

# Verbose container logs
podman logs --timestamps web

# Inspect container details
podman inspect web --format '{{.State.Status}}'

# View container events
podman events

# Check storage usage
podman system df -v

# Test network connectivity
podman run --rm alpine ping -c 3 8.8.8.8

# Verify rootless setup
podman unshare cat /proc/self/uid_map

Related Topics

The following topics complement this Podman cheatsheet and would be valuable additions:

  1. Podman Quadlets and systemd - The production half of this sheet: Quadlet unit types, multi-container pods, networks, timers, sdnotify readiness, and auto-updates
  2. Docker - Understand the Docker ecosystem that Podman is compatible with, including Dockerfile syntax and Docker Compose patterns
  3. Kubernetes - Podman's pod concept directly mirrors Kubernetes pods; essential for using podman kube generate and podman kube play
  4. systemd - Unit files, targets, timers, drop-ins, and journald in depth
  5. Container Security - Image scanning, rootless security benefits, SELinux/AppArmor integration, and security best practices
  6. Buildah - Podman's underlying build tool for advanced image building without a Containerfile
  7. Skopeo - Companion tool for inspecting and copying container images between registries without pulling them locally