Podman for Self-Hosting: Complete Setup Guide

What Is Podman?

Podman is a daemonless, rootless container engine developed by Red Hat that serves as a drop-in replacement for Docker. Unlike Docker, Podman doesn’t require a background daemon running as root — each container runs as a direct child process of the user who started it. This makes Podman fundamentally more secure for self-hosting setups where you want to minimize the attack surface.

Podman official site

Prerequisites

  • A Linux server (Ubuntu 22.04+, Fedora 39+, Debian 12+, or RHEL 9+)
  • At least 2 GB of RAM
  • 10 GB of free disk space
  • Root access for initial setup (Podman itself runs rootless after installation)

Installation

Ubuntu / Debian

sudo apt update
sudo apt install -y podman

On Ubuntu 22.04, the default repos ship Podman 3.4.x. For Podman 5.x, add the upstream repository:

# Ubuntu 24.04+ includes Podman 5.x in default repos
sudo apt update
sudo apt install -y podman
podman --version

Fedora / RHEL

# Fedora (Podman is pre-installed on Fedora 39+)
sudo dnf install -y podman

# RHEL 9+
sudo dnf install -y podman

Verify Installation

podman --version
# Expected: podman version 5.8.0

podman info --format '{{.Host.Security.Rootless}}'
# Expected: true

Podman vs Docker: Key Differences

FeaturePodmanDocker
DaemonNo daemon (fork-exec model)Requires dockerd daemon
RootlessNative, first-classAvailable but secondary
SocketPer-user socketSystem-wide socket
Composepodman-compose or podman composedocker compose
Systemd integrationNative (Quadlet)Requires manual setup
OCI compliantYesYes
CLI compatibleDrop-in replacement for docker CLIN/A

Running Containers with Podman

Podman’s CLI is intentionally Docker-compatible. Most docker commands work by replacing docker with podman:

# Pull an image
podman pull docker.io/library/nginx:1.28.2

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

# List running containers
podman ps

# View logs
podman logs nginx

# Stop and remove
podman stop nginx
podman rm nginx

Podman Compose

Podman supports Docker Compose files through two methods:

Podman 5.x includes a built-in compose subcommand that wraps an external compose provider (docker-compose or podman-compose):

# Install docker-compose as the backend
sudo apt install -y docker-compose

# Or install podman-compose via pip
pip3 install podman-compose

# Run compose files
podman compose up -d
podman compose down
podman compose logs

Method 2: podman-compose Standalone

pip3 install podman-compose

# Use directly
podman-compose up -d
podman-compose down

Example: Running a Self-Hosted App

Create a docker-compose.yml (yes, same filename — Podman reads Docker Compose files):

services:
  uptime-kuma:
    image: docker.io/louislam/uptime-kuma:1.23.16
    container_name: uptime-kuma
    ports:
      - "3001:3001"
    volumes:
      - uptime-kuma-data:/app/data
    restart: unless-stopped

volumes:
  uptime-kuma-data:
podman compose up -d

Rootless Containers

Rootless mode is Podman’s killer feature. Containers run entirely under your user account with no root privileges anywhere in the chain.

Enable Rootless Prerequisites

# Verify your user has subuids/subgids allocated
grep $USER /etc/subuid
grep $USER /etc/subgid
# Expected: username:100000:65536

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

Port Binding in Rootless Mode

Rootless containers cannot bind to ports below 1024 by default. Options:

# Option 1: Use ports above 1024 (recommended)
podman run -d -p 8080:80 nginx:1.28.2

# Option 2: Allow unprivileged port binding system-wide
sudo sysctl -w net.ipv4.ip_unprivileged_port_start=80
# Make permanent:
echo "net.ipv4.ip_unprivileged_port_start=80" | sudo tee /etc/sysctl.d/podman-ports.conf

Storage Location

Rootless containers store images and volumes under ~/.local/share/containers/ instead of /var/lib/docker/.

Systemd Integration with Quadlet

Podman integrates natively with systemd through Quadlet files. Instead of managing containers with compose, you can define them as systemd units:

Create ~/.config/containers/systemd/uptime-kuma.container:

[Unit]
Description=Uptime Kuma monitoring
After=network-online.target

[Container]
Image=docker.io/louislam/uptime-kuma:1.23.16
ContainerName=uptime-kuma
PublishPort=3001:3001
Volume=uptime-kuma-data.volume:/app/data

[Service]
Restart=always

[Install]
WantedBy=default.target

Create ~/.config/containers/systemd/uptime-kuma-data.volume:

[Volume]

Activate it:

systemctl --user daemon-reload
systemctl --user start uptime-kuma
systemctl --user enable uptime-kuma

# Enable lingering so services run without an active login session
loginctl enable-linger $USER

Migrating from Docker to Podman

Step 1: Install Podman

Follow the installation instructions above for your distribution.

Step 2: Alias Docker to Podman (Optional)

echo 'alias docker=podman' >> ~/.bashrc
source ~/.bashrc

Step 3: Transfer Images

# On Docker host, save images
docker save myimage:tag -o myimage.tar

# On Podman host, load images
podman load -i myimage.tar

Step 4: Run Existing Compose Files

Most docker-compose.yml files work without changes:

podman compose up -d

Common Migration Gotchas

  • Docker socket references: Apps that mount /var/run/docker.sock (like Portainer, Watchtower (deprecated)) need the Podman socket instead: /run/user/$(id -u)/podman/podman.sock
  • Network mode: network_mode: host works the same, but bridge networking uses different default subnets
  • Named volumes: Stored in a different location (~/.local/share/containers/storage/volumes/)
  • Build context: podman build works identically to docker build

Enabling the Podman Socket (Docker API Compatibility)

Some tools expect a Docker-compatible API socket. Podman provides one:

# Rootless socket
systemctl --user enable --now podman.socket

# The socket is at:
# /run/user/$(id -u)/podman/podman.sock

# Root socket (if needed)
sudo systemctl enable --now podman.socket
# Socket at: /run/podman/podman.sock

For apps that expect DOCKER_HOST:

export DOCKER_HOST=unix:///run/user/$(id -u)/podman/podman.sock

Networking

Default Network

Podman uses netavark (Podman 5.x) or cni (older versions) for container networking:

# List networks
podman network ls

# Create a custom network
podman network create mynet

# Run a container on a custom network
podman run -d --network mynet --name app myimage:tag

DNS Resolution

Containers on the same custom network can resolve each other by container name, similar to Docker.

Backup

Container data in Podman is stored in volumes under ~/.local/share/containers/storage/volumes/ (rootless) or /var/lib/containers/storage/volumes/ (rootful).

Back up volumes:

# Find volume location
podman volume inspect myvolume --format '{{.Mountpoint}}'

# Back up the data
tar -czf myvolume-backup.tar.gz -C $(podman volume inspect myvolume --format '{{.Mountpoint}}') .

For a comprehensive backup approach, see Backup Strategy.

Troubleshooting

Rootless Container Cannot Access Network

Symptom: Container starts but cannot reach the internet. Fix: Ensure slirp4netns or pasta is installed:

sudo apt install -y slirp4netns
# Or for Podman 5.x with pasta:
sudo apt install -y passt

Permission Denied on Volume Mounts

Symptom: Permission denied errors when writing to bind-mounted directories. Fix: Append :Z to the volume mount for SELinux systems, or use podman unshare to fix ownership:

podman unshare chown -R 1000:1000 /path/to/data

Podman Compose Fails with “No Such Command”

Symptom: podman compose returns an error about missing compose provider. Fix: Install a compose backend:

pip3 install podman-compose
# Or install docker-compose
sudo apt install docker-compose

Container Won’t Start After Reboot

Symptom: Containers with restart: unless-stopped don’t start after a reboot. Fix: Rootless Podman containers don’t auto-restart without systemd integration. Use Quadlet or enable the Podman restart service:

systemctl --user enable podman-restart
loginctl enable-linger $USER

“Error: short-name resolution enforced”

Symptom: Pulling images fails because Podman requires fully-qualified image names. Fix: Use the full registry path:

# Instead of:
podman pull nginx:1.28.2
# Use:
podman pull docker.io/library/nginx:1.28.2

Or add Docker Hub as a default search registry in /etc/containers/registries.conf:

unqualified-search-registries = ["docker.io"]

Resource Requirements

  • RAM: 50 MB idle (Podman itself), plus whatever your containers use
  • CPU: Minimal — no daemon overhead
  • Disk: ~200 MB for Podman + dependencies, plus container images and volumes

Verdict

Podman is the best Docker alternative for security-conscious self-hosters. Its rootless-by-default approach eliminates the biggest security concern with Docker (a root-running daemon with access to everything). The CLI compatibility means most Docker tutorials and Compose files work without changes. The main trade-off is that some Docker-specific tools (Portainer, Watchtower (deprecated)) require extra configuration to work with the Podman socket, and rootless networking has quirks with low ports. If you’re starting fresh or want better security, use Podman. If you have a large existing Docker setup with tools that depend on the Docker socket, the migration effort may not be worth it.

FAQ

Can I run Docker and Podman on the same machine?

Yes. They use separate storage locations and don’t conflict. However, they cannot share images or volumes directly — you’d need to export/import between them.

Does Podman support Docker Compose files?

Yes. Podman reads standard docker-compose.yml files through podman compose or podman-compose. Most files work without modification.

Is Podman slower than Docker?

No. Container runtime performance is identical since both use the same underlying OCI runtime (runc/crun). Podman can be marginally faster for starting containers because there’s no daemon overhead.

Does Podman work with Kubernetes?

Yes. Podman can generate Kubernetes YAML from running containers (podman generate kube) and play Kubernetes YAML files (podman play kube), which is useful for testing locally before deploying to a cluster.

Can I use Portainer with Podman?

Yes, but you need to enable the Podman socket and point Portainer to it. Set DOCKER_HOST to the Podman socket path or mount it as a volume.

Comments