Podman as Docker Alternative

Podman (Pod Manager) is a daemonless container engine that provides Docker-compatible functionality without requiring a privileged daemon process. This guide covers installing Podman, configuring rootless containers, managing pods, achieving Docker compatibility, integrating with systemd, and deploying containerized applications. Podman offers superior security through rootless operation while maintaining API compatibility with Docker, making it an attractive alternative for many deployment scenarios.

Table of Contents

Understanding Podman

Podman is a container engine and pod orchestration tool that runs containers as regular user processes without a daemon. Key characteristics:

  • Daemonless architecture: No privileged service running constantly
  • Rootless capable: Containers run as unprivileged users
  • Docker API compatible: Works with Docker CLI commands
  • Supports pods: Groups related containers together
  • Systemd integration: Native service unit management
  • OCI compliant: Uses standard container formats
# Check Podman version
podman --version

# View system information
podman info

# Check Podman configuration
podman system info | head -20

# Display Podman help
podman --help

Architectural differences:

  • Docker: Client-server with privileged daemon
  • Podman: Daemonless architecture, direct container process management
  • Security: Podman rootless eliminates privilege escalation vector
  • Process management: Podman integrates with user processes

Installing Podman

Install Podman across different Linux distributions.

Ubuntu/Debian installation:

# Update package manager
sudo apt-get update

# Install Podman
sudo apt-get install -y podman

# Verify installation
podman --version

# Install additional tools
sudo apt-get install -y \
    podman-docker \
    podman-compose \
    slirp4netns \
    uidmap

# Check system capabilities
podman system info | grep rootless

CentOS/RHEL installation:

# Enable container tools repository
sudo dnf install -y centos-release-stream-container-tools

# Install Podman
sudo dnf install -y podman podman-compose

# Verify installation
podman --version

# Enable podman socket for API access
sudo systemctl enable --now podman.socket

# Check socket availability
sudo ls -la /run/podman/podman.sock

Fedora installation:

# Install Podman (usually pre-installed)
sudo dnf install -y podman

# Install additional components
sudo dnf install -y \
    podman-docker \
    podman-compose \
    slirp4netns \
    fuse-overlayfs

# Verify all components installed
podman --version
podman-compose --version

Alpine Linux installation:

# Update apk cache
apk update

# Install Podman
apk add podman

# Install additional tools
apk add podman-docker podman-compose slirp4netns fuse-overlayfs

# Check installation
podman --version

Verify installation and setup:

# Test basic Podman functionality
podman run --rm alpine echo "Podman works!"

# Check for required namespaces
grep subuid /etc/subuid
grep subgid /etc/subgid

# View runtime information
podman info --format="{{.Host.Runtime}}"

Podman vs Docker

Understand key differences between Podman and Docker.

Comparison table:

# Feature comparison
cat > podman_vs_docker.txt <<'EOF'
Feature              | Podman          | Docker
Architecture         | Daemonless      | Daemon-based
Privilege            | Rootless ready  | Requires root
Pods                 | Native support  | Via Kubernetes
API compatibility    | High            | Source compatible
Image format         | OCI             | OCI
Network driver       | CNI             | libnetwork
Orchestration        | Systemd         | Swarm/K8s
Installation         | Lightweight     | Full daemon
Performance          | Lower overhead  | Established
Community            | Growing         | Large
EOF

cat podman_vs_docker.txt

Docker command to Podman migration:

# Create docker alias if switching from Docker
alias docker=podman

# Or setup docker client pointing to Podman socket
export DOCKER_HOST=unix:///run/podman/podman.sock

# Run equivalent commands
podman run -d --name web -p 8080:80 nginx:latest
podman ps
podman logs web
podman exec web curl localhost

# Stop and remove
podman stop web
podman rm web

Pulling and running containers:

# Pull image from Docker Hub
podman pull alpine:latest

# List images
podman images

# Run container
podman run -it alpine:latest sh

# Run in background
podman run -d --name myapp -p 5000:5000 myapp:latest

# Check running containers
podman ps

# View logs
podman logs myapp

# Execute command in container
podman exec myapp curl localhost:5000

Rootless Container Operations

Run containers as unprivileged users for enhanced security.

Setup rootless Podman:

# Check user capabilities
cat /etc/subuid
cat /etc/subgid

# If empty, configure user subordinate IDs
sudo usermod --add-subuids 100000-165535 $USER
sudo usermod --add-subgids 100000-165535 $USER

# Verify configuration
grep $USER /etc/subuid /etc/subgid

# Initialize rootless Podman
podman system migrate

# Run test container as regular user
podman run --rm alpine echo "Rootless Podman works!"

# Check running as user
ps aux | grep conmon

Rootless networking configuration:

# Check rootless network mode
podman info | grep -i network

# Verify slirp4netns available
which slirp4netns

# Create network for rootless containers
podman network create mynet

# Run container on custom network
podman run -d \
  --name web \
  --network mynet \
  -p 8080:80 \
  nginx:latest

# Test connectivity
curl http://localhost:8080

# Inspect network details
podman network inspect mynet

# List networks
podman network ls

User namespace remapping:

# Check current user mapping
cat /etc/subuid | head

# View subordinate user IDs
grep $USER /etc/subuid

# Container processes map to high UID ranges
# Process shows as UID 0 inside container
# But runs as high UID on host system

# Example: Start container and check UIDs
podman run -d --name test alpine sleep 1000

# Inside container
podman exec test id  # uid=0(root) gid=0(root)

# On host
ps aux | grep sleep | grep -v grep  # Shows high UID

Rootless socket activation:

# Enable user service for Podman socket
systemctl --user enable podman.socket
systemctl --user start podman.socket

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

# Set DOCKER_HOST for Docker CLI compatibility
export DOCKER_HOST=unix:///run/user/$(id -u)/podman/podman.sock

# Test with Docker CLI
docker ps

# Persist environment variable
echo 'export DOCKER_HOST=unix:///run/user/$(id -u)/podman/podman.sock' >> ~/.bashrc
source ~/.bashrc

Managing Pods with Podman

Create and manage pods, which group related containers.

Create and manage pods:

# Create a pod
podman pod create \
  --name myapp-pod \
  -p 8080:8080 \
  -p 5432:5432

# List pods
podman pod ls

# Inspect pod details
podman pod inspect myapp-pod

# Add container to pod
podman run -d \
  --pod myapp-pod \
  --name web \
  nginx:latest

# Add another container to same pod
podman run -d \
  --pod myapp-pod \
  --name api \
  myapi:latest

# Containers in pod share network namespace
# Can communicate via localhost

# View pod details
podman pod stats myapp-pod

# Stop pod (stops all containers)
podman pod stop myapp-pod

# Remove pod
podman pod rm myapp-pod

Multi-container pod example:

# Create pod with specific configuration
podman pod create \
  --name webapp \
  --publish 80:80 \
  --publish 443:443 \
  --dns 8.8.8.8 \
  --add-host db.internal:10.0.0.2

# Create database container in pod
podman run -d \
  --pod webapp \
  --name postgres \
  --env POSTGRES_PASSWORD=secret \
  postgres:15

# Create app container in pod
podman run -d \
  --pod webapp \
  --name app \
  --env DATABASE_URL=postgresql://user:secret@localhost/dbname \
  myapp:latest

# Containers can communicate
podman exec app psql -h localhost -U user

# View all containers in pod
podman pod ps
podman pod ps -a --format "{{.Name}}\t{{.Containers}}"

Pod networking:

# Pods have their own network namespace
# Containers in pod share loopback and eth0

# Expose pod ports
podman pod create \
  --name myapp \
  --publish 3000:3000 \
  --publish 5000:5000

# Multiple containers can use different ports
podman run -d --pod myapp --name web -p 3000:3000 node-app:latest
podman run -d --pod myapp --name api python-api:latest

# Access via localhost
curl http://localhost:3000/
curl http://localhost:5000/api

# Container-to-container communication via localhost:port

Docker Compatibility

Leverage Docker-compatible tools and workflows with Podman.

Docker CLI compatibility:

# podman-docker package provides docker alias
sudo apt-get install podman-docker

# Docker commands work with Podman
docker run -d --name web -p 8080:80 nginx:latest
docker ps
docker logs web
docker exec web curl localhost
docker stop web
docker rm web

# Volume operations
docker volume create mydata
docker run -d -v mydata:/data nginx:latest

# Image operations
docker build -t myapp:latest .
docker tag myapp:latest myregistry.com/myapp:latest
docker push myregistry.com/myapp:latest
docker pull alpine:latest

Dockerfile compatibility:

# Build images with Podman (Dockerfile syntax identical)
cat > Dockerfile <<'EOF'
FROM node:18-alpine
WORKDIR /app
COPY package*.json ./
RUN npm ci --only=production
COPY . .
EXPOSE 3000
CMD ["node", "server.js"]
EOF

# Build image with Podman
podman build -t myapp:latest .

# Tag and push to registry
podman tag myapp:latest quay.io/username/myapp:latest
podman push quay.io/username/myapp:latest

# Pull from registry
podman pull quay.io/username/myapp:latest

# Run image
podman run -d -p 3000:3000 myapp:latest

API compatibility:

# Podman provides Docker-compatible API
sudo systemctl start podman.socket

# Set Docker client to use Podman socket
export DOCKER_HOST=unix:///run/podman/podman.sock

# Docker client commands work with Podman
docker ps
docker run alpine echo "Using Podman via Docker API"
docker images

# REST API compatibility
curl -s --unix-socket /run/podman/podman.sock \
  http://localhost/v1.0.0/libpod/info | jq

# Use Docker client library with Podman
python3 -c "import docker; client = docker.from_env(); print(client.version())"

Systemd Integration

Run containers as systemd services for automatic startup and management.

Create systemd service files:

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

# Create service file for container
cat > ~/.config/systemd/user/myapp.service <<'EOF'
[Unit]
Description=My Application Container
After=network-online.target
Wants=network-online.target

[Service]
Type=notify
NotifyAccess=main
ExecStart=/usr/bin/podman run \
    --rm \
    --name myapp \
    -p 5000:5000 \
    -v /home/$USER/myapp/data:/data \
    myapp:latest
ExecStop=/usr/bin/podman stop myapp
Restart=always
RestartSec=10

[Install]
WantedBy=default.target
EOF

# Enable and start service
systemctl --user daemon-reload
systemctl --user enable myapp.service
systemctl --user start myapp.service

# Check service status
systemctl --user status myapp.service

# View service logs
journalctl --user -u myapp.service -f

Advanced systemd integration:

# Service with multiple containers
cat > ~/.config/systemd/user/myapp.service <<'EOF'
[Unit]
Description=Multi-Container Application
After=network-online.target
Wants=network-online.target

[Service]
Type=notify
NotifyAccess=main
ExecStartPre=/usr/bin/podman pod create \
    --name myapp-pod \
    -p 8080:80 \
    -p 5432:5432

ExecStart=/usr/bin/podman run \
    --rm \
    --pod myapp-pod \
    --name app \
    myapp:latest

ExecStart=/usr/bin/podman run \
    --rm \
    --pod myapp-pod \
    --name db \
    postgres:15

ExecStop=/usr/bin/podman pod stop myapp-pod
ExecStopPost=/usr/bin/podman pod rm myapp-pod

Restart=on-failure
RestartSec=10

[Install]
WantedBy=multi-user.target
EOF

# System-wide service (requires root)
sudo tee /etc/systemd/system/myapp.service > /dev/null <<'EOF'
[Unit]
Description=My Application Container
After=network-online.target

[Service]
Type=notify
ExecStart=/usr/bin/podman run --rm --name myapp myapp:latest
ExecStop=/usr/bin/podman stop myapp
Restart=always

[Install]
WantedBy=multi-user.target
EOF

sudo systemctl daemon-reload
sudo systemctl enable myapp.service
sudo systemctl start myapp.service

Podman Compose

Use docker-compose files with Podman for multi-container applications.

Basic Podman Compose:

# Install podman-compose if not included
sudo apt-get install podman-compose
# or pip install podman-compose

# Create compose file
cat > docker-compose.yml <<'EOF'
version: '3.9'

services:
  web:
    image: nginx:alpine
    ports:
      - "80:80"
    volumes:
      - ./html:/usr/share/nginx/html
    depends_on:
      - app

  app:
    build: .
    ports:
      - "5000:5000"
    environment:
      - DATABASE_URL=postgresql://user:password@db:5432/mydb
    depends_on:
      - db

  db:
    image: postgres:15-alpine
    environment:
      POSTGRES_DB: mydb
      POSTGRES_USER: user
      POSTGRES_PASSWORD: password
    volumes:
      - db_data:/var/lib/postgresql/data

volumes:
  db_data:

EOF

# Start services with Podman Compose
podman-compose up -d

# Check services
podman-compose ps

# View logs
podman-compose logs -f

# Stop services
podman-compose down

Advanced compose configurations:

cat > docker-compose.yml <<'EOF'
version: '3.9'

services:
  web:
    image: nginx:alpine
    container_name: web_container
    restart: unless-stopped
    ports:
      - "80:80"
      - "443:443"
    volumes:
      - ./nginx.conf:/etc/nginx/nginx.conf
      - ./html:/usr/share/nginx/html
    networks:
      - frontend

  api:
    build:
      context: ./api
      dockerfile: Dockerfile
    container_name: api_container
    restart: unless-stopped
    ports:
      - "5000:5000"
    environment:
      - DEBUG=false
      - DATABASE_URL=postgresql://user:pass@db:5432/mydb
    depends_on:
      - db
    networks:
      - frontend
      - backend

  db:
    image: postgres:15-alpine
    container_name: db_container
    restart: unless-stopped
    environment:
      POSTGRES_DB: mydb
      POSTGRES_USER: user
      POSTGRES_PASSWORD: pass
    volumes:
      - db_data:/var/lib/postgresql/data
      - ./init.sql:/docker-entrypoint-initdb.d/init.sql
    networks:
      - backend

networks:
  frontend:
    driver: bridge
  backend:
    driver: bridge

volumes:
  db_data:

EOF

# Deploy with Podman Compose
podman-compose up -d

# Scale service
podman-compose up -d --scale api=3

# Remove everything
podman-compose down -v

Advanced Podman Features

Implement advanced Podman capabilities for production deployments.

Custom storage driver:

# Configure alternate storage driver
sudo tee /etc/containers/storage.conf <<'EOF'
[storage]
driver = "overlay"
graphroot = "/var/lib/containers/storage"
runroot = "/run/containers/storage"

[storage.options]
mount_program = "/usr/bin/fuse-overlayfs"
EOF

# Check storage configuration
podman info --format='{{.Store}}'

# Verify storage driver
podman info | grep StorageDriver

Image management:

# Save image to tar
podman save myapp:latest -o myapp.tar

# Load image from tar
podman load -i myapp.tar

# Export container filesystem
podman export <container-id> -o container.tar

# Import container image
podman import container.tar myimage:latest

# List all images with details
podman images --all --format "table {{.Repository}}\t{{.Tag}}\t{{.Size}}"

# Remove unused images
podman image prune -a

# Remove dangling images
podman image prune

Security features:

# Run container with read-only root filesystem
podman run -d --read-only --tmpfs /tmp myapp:latest

# Use security labels
podman run -d --security-opt label=type:svirt_apache_t myapp:latest

# Run with specific capabilities
podman run -d --cap-add NET_ADMIN --cap-drop ALL myapp:latest

# Use seccomp profiles
podman run -d --security-opt seccomp=/path/to/profile.json myapp:latest

# Check security options
podman inspect <container-id> | grep -A 5 SecurityOpt

Conclusion

Podman represents a modern approach to container management that prioritizes security, compatibility, and simplicity. Its daemonless architecture eliminates privilege escalation vectors, while Docker CLI compatibility ensures minimal learning curve for Docker users. The native pod support enables simplified multi-container deployments, and systemd integration provides robust service management. Whether migrating from Docker or starting fresh, Podman delivers production-ready container functionality with enhanced security posture. As container technology evolves, Podman's design choices align with emerging industry standards and practices. Start with basic container operations, progress to pod management, and leverage systemd integration for production deployments.