$linuxjunkies
>

Install and Use Podman (Docker without a daemon)

Run OCI containers without a daemon using Podman. Covers rootless setup, podman-compose, and systemd Quadlet units for production-grade container management.

IntermediateUbuntuDebianFedoraArch9 min readUpdated June 7, 2026

Before you start

  • A 64-bit Linux system with kernel 5.11+ (for full rootless namespace support)
  • A non-root user account with sudo privileges
  • systemd 239+ (standard on all supported distros)

Podman is a daemonless container engine that runs OCI-compatible containers without requiring a root-level background service. Every container you run is a direct child of your shell or systemd unit — no central daemon, no single point of failure. Rootless mode means a compromised container cannot escape to root on the host. If you've used Docker, most commands are a drop-in replacement; alias docker=podman genuinely works for day-to-day use.

Installing Podman

Debian / Ubuntu

Podman 4.x is available in Ubuntu 22.04+ and Debian 12+ official repos. For older releases, add the Kubic repository.

sudo apt update && sudo apt install -y podman

Fedora / RHEL / Rocky Linux

Podman ships by default on Fedora and is in the AppStream repo on RHEL 8+.

sudo dnf install -y podman

Arch Linux

sudo pacman -S podman

Confirm the version after install:

podman --version
# podman version 5.x.x (output will vary)

Enabling Rootless Containers

Rootless Podman runs containers entirely within your user's namespace. The kernel needs subordinate UID/GID ranges assigned to your account. Modern distros configure these automatically at user creation, but verify:

grep "$(whoami)" /etc/subuid /etc/subgid

If your username is absent, add the ranges manually (replace alice with your username):

sudo usermod --add-subuids 100000-165535 --add-subgids 100000-165535 alice

Then reload the user namespace mappings:

podman system migrate

Enable and start the rootless socket for your user (needed by some tools, including podman-compose):

systemctl --user enable --now podman.socket

Running Your First Rootless Container

Pull and run a container as your normal user — no sudo:

podman run --rm -it docker.io/library/alpine:latest sh

Inside the container, id reports uid=0(root), but that maps to your unprivileged UID on the host. To confirm this from a second terminal while the container runs:

podman top <container-id> huser user
# HUSER  USER
# alice  root

HUSER is the real host user — never root. That's the security boundary rootless gives you.

Persistent Containers and Volumes

Run a named container with a bind-mounted volume:

podman run -d \
  --name my-nginx \
  -p 8080:80 \
  -v "$HOME/www:/usr/share/nginx/html:Z" \
  docker.io/library/nginx:stable-alpine

The :Z label relabels the volume for SELinux. Use :z for shared volumes on SELinux-enforcing systems (Fedora, RHEL, Rocky). On non-SELinux systems it is ignored safely.

podman ps
podman logs my-nginx
podman stop my-nginx && podman rm my-nginx

Multi-Container Apps with podman-compose

podman-compose reads the same compose.yaml format as Docker Compose and wraps Podman calls under the hood.

Install podman-compose

# Debian/Ubuntu
sudo apt install -y podman-compose

# Fedora/RHEL
sudo dnf install -y podman-compose

# Arch
sudo pacman -S podman-compose

# Any distro via pip
pip install --user podman-compose

Example compose.yaml

cat > compose.yaml <<'EOF'
services:
  web:
    image: docker.io/library/nginx:stable-alpine
    ports:
      - "8080:80"
    volumes:
      - ./html:/usr/share/nginx/html:Z
  db:
    image: docker.io/library/postgres:16-alpine
    environment:
      POSTGRES_PASSWORD: example
    volumes:
      - pgdata:/var/lib/postgresql/data
volumes:
  pgdata:
EOF
podman-compose up -d
podman-compose logs -f
podman-compose down

Systemd Integration with Quadlet

Quadlet (merged into Podman 4.4+) lets you define containers in declarative .container unit files that systemd manages directly. This is the modern, production-grade replacement for podman generate systemd, which is now deprecated.

Creating a Quadlet Unit

For a rootless user service, place unit files in ~/.config/containers/systemd/. For a system-wide service, use /etc/containers/systemd/.

mkdir -p ~/.config/containers/systemd/
cat > ~/.config/containers/systemd/my-nginx.container <<'EOF'
[Unit]
Description=My nginx container
After=network-online.target

[Container]
Image=docker.io/library/nginx:stable-alpine
PublishPort=8080:80
Volume=%h/www:/usr/share/nginx/html:Z
AutoUpdate=registry

[Service]
Restart=always
TimeoutStartSec=60

[Install]
WantedBy=default.target
EOF

The %h specifier expands to your home directory. AutoUpdate=registry enables automatic image updates via podman auto-update.

Loading and Starting the Unit

Quadlet generates the actual systemd unit at daemon-reload time:

systemctl --user daemon-reload
systemctl --user start my-nginx.service
systemctl --user status my-nginx.service

Enable it to start on login (or at boot with lingering enabled):

systemctl --user enable my-nginx.service

# Allow the service to run even when you're not logged in:
sudo loginctl enable-linger "$(whoami)"

System-Wide Quadlet Units

For root-level services drop the --user flag and use /etc/containers/systemd/:

sudo systemctl daemon-reload
sudo systemctl enable --now my-nginx.service

Verification

Check your rootless setup end-to-end:

# Confirm no daemon is running
systemctl status podman 2>/dev/null || echo "No system daemon — expected"

# List running user containers
podman ps

# Confirm the Quadlet service is active
systemctl --user is-active my-nginx.service

# Hit the nginx port
curl -s http://localhost:8080 | head -5

Troubleshooting

"slirp4netns" or "pasta" not found

Rootless networking requires a userspace network helper. Install the appropriate package:

# Debian/Ubuntu
sudo apt install -y slirp4netns

# Fedora/RHEL
sudo dnf install -y slirp4netns

# Arch
sudo pacman -S slirp4netns

Podman 5+ prefers pasta (from the passt package) over slirp4netns for better performance. Install passt if available on your distro.

Permission denied on bind mounts with SELinux

Always add :Z (private) or :z (shared) to volume mounts on SELinux-enforcing systems. Forgetting this is the most common cause of silent permission failures inside containers on Fedora and RHEL.

Quadlet unit not appearing after daemon-reload

Check for syntax errors in your .container file:

/usr/lib/systemd/system-generators/podman-system-generator --user --dryrun

Look for parse warnings in the output. Common mistakes: wrong section headers, missing Image= key, or placing the file in the wrong directory.

Container works but dies after logout

You need lingering enabled. Without it, your user's systemd instance is terminated on logout, killing all user services:

sudo loginctl enable-linger "$(whoami)"
tested on:Ubuntu 24.04Fedora 40Arch rollingRocky 9

Frequently asked questions

Is Podman a drop-in replacement for Docker?
For most CLI usage yes — the command syntax is nearly identical and aliasing docker to podman works for everyday tasks. Differences appear with Docker Compose (use podman-compose), Docker Desktop GUI tooling, and features that assume a persistent daemon like docker events streaming.
What is the difference between rootless and rootful Podman?
Rootless runs all container processes inside a user namespace mapped to your unprivileged UID, so a container escape cannot grant host root access. Rootful (run with sudo) gives containers more kernel capabilities and easier bind-mount permissions but carries greater security risk.
Why use Quadlet instead of podman generate systemd?
podman generate systemd is deprecated as of Podman 4.4 and may be removed in a future release. Quadlet is the official replacement: you write a small declarative unit file and systemd generates the full service, rather than maintaining a sprawling auto-generated unit.
Can I use Docker Hub images with Podman?
Yes, but you must specify the full registry path: docker.io/library/nginx rather than just nginx. Configure /etc/containers/registries.conf to add docker.io as an unqualified search registry if you want short names to resolve automatically.
How do I automatically update container images managed by Quadlet?
Add AutoUpdate=registry to the [Container] section of your .container file, then enable the podman-auto-update systemd timer with systemctl --user enable --now podman-auto-update.timer. It checks for newer images and restarts affected services.

Related guides