$linuxjunkies
>

How to Run Rootless Containers with Podman

Run OCI containers without root or a daemon. Learn Podman installation, user namespace setup, rootless networking, and systemd Quadlet unit files for production use.

IntermediateUbuntuDebianFedoraArch9 min readUpdated May 26, 2026

Before you start

  • A Linux host with a kernel version 5.11 or later (earlier kernels may lack full user namespace support)
  • A non-root user account with sudo access for initial setup steps
  • Basic familiarity with the command line and container concepts (images, containers, volumes)

Podman lets you build and run OCI-compatible containers without a root-level daemon. Each container runs inside your user's own namespace, so a breakout can't trivially escalate to root on the host. This guide walks through installing Podman, configuring the user namespace stack, pulling and running images as a normal user, and wiring containers into systemd so they survive reboots — all without touching the Docker socket or granting extra privileges.

Podman vs Docker: What Actually Differs

Docker relies on a persistent daemon (dockerd) running as root. Podman is daemonless — each podman invocation is a direct fork/exec. The practical consequences:

  • No shared socket. There is no /var/run/docker.sock to protect or leak.
  • Rootless by default. Images and container state live under ~/.local/share/containers, not /var/lib/docker.
  • Drop-in CLI compatibility. Most docker flags work unchanged with podman. The project ships an alias script if needed.
  • Compose support. podman-compose (community) or Podman's native podman compose (v4.7+) handles Compose files without Docker.

The trade-off: rootless containers can't bind ports below 1024 without extra sysctl configuration, and some images that assume root inside the container require namespace mapping tweaks.

Install Podman

Debian / Ubuntu

sudo apt update
sudo apt install -y podman

Ubuntu 22.04 LTS and Debian 12+ ship Podman 3.x or 4.x in their default repos. For the latest 5.x series on Ubuntu, add the Kubic repo or use the upstream Debian unstable backport — check podman.io/docs/installation for the current PPA.

Fedora / RHEL 9 / Rocky Linux 9

sudo dnf install -y podman

Fedora ships a very current Podman. On RHEL/Rocky 9 the default stream is Podman 4.x; enable the container-tools:rhel9 module if you need a specific version.

Arch Linux

sudo pacman -S podman

Configure User Namespaces

Rootless containers depend on the kernel's user namespace feature and a range of subordinate UIDs/GIDs mapped to your user account.

Verify the kernel supports user namespaces

cat /proc/sys/kernel/unprivileged_userns_clone

The output should be 1. On Debian (not Ubuntu), this is sometimes 0 by default. Enable it persistently:

echo 'kernel.unprivileged_userns_clone=1' | sudo tee /etc/sysctl.d/99-userns.conf
sudo sysctl --system

Set up subordinate UID/GID ranges

Check whether your user already has entries in /etc/subuid and /etc/subgid:

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

If nothing is returned, add ranges (replace alice with your username):

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

Then tell Podman to re-read the mapping:

podman system migrate

Pull and Run Your First Rootless Container

No sudo needed from here on for day-to-day container work.

podman pull docker.io/library/nginx:alpine
podman run -d --name web -p 8080:80 docker.io/library/nginx:alpine

Port 8080 on the host maps to port 80 inside the container. Rootless Podman cannot bind ports below 1024 unless you lower the kernel's port range limit:

echo 'net.ipv4.ip_unprivileged_port_start=80' | sudo tee /etc/sysctl.d/99-unpriv-port.conf
sudo sysctl --system

After that change, -p 80:80 works for your user without sudo.

Verify the container is running

podman ps

Expected output (shortened — yours will show actual IDs and timestamps):

CONTAINER ID  IMAGE                           COMMAND               STATUS        PORTS
a3f8c2d1e9b0  docker.io/library/nginx:alpine  nginx -g daemon o...  Up 2 minutes  0.0.0.0:8080->80/tcp
curl -s -o /dev/null -w "%{http_code}" http://localhost:8080

Returns 200 if nginx is serving correctly.

Manage Container Lifecycle with systemd

Podman integrates tightly with systemd through Quadlet (Podman 4.4+), which replaces the older podman generate systemd approach. Quadlet reads .container unit files and generates the actual systemd units automatically.

Create the Quadlet unit file

User-level Quadlet files live in ~/.config/containers/systemd/.

mkdir -p ~/.config/containers/systemd
nano ~/.config/containers/systemd/web.container

Paste the following:

[Unit]
Description=Nginx web container
After=network-online.target

[Container]
Image=docker.io/library/nginx:alpine
PublishPort=8080:80
Volume=%h/www:/usr/share/nginx/html:Z
Environment=NGINX_ENTRYPOINT_QUIET_LOGS=1

[Service]
Restart=always

[Install]
WantedBy=default.target

The :Z label on the volume mount sets the correct SELinux context on Fedora/RHEL systems. It is harmless on non-SELinux hosts. %h expands to your home directory.

Enable and start the unit

Reload the user daemon so it picks up the new Quadlet file:

systemctl --user daemon-reload
systemctl --user enable --now web.service

Enable lingering so the container starts at boot without a login session

loginctl enable-linger "$(whoami)"

Without lingering, your user's systemd instance is killed when the last session closes, taking all user services with it.

Check service status

systemctl --user status web.service
journalctl --user -u web.service -n 30 --no-pager

Useful Day-to-Day Commands

  • Stop and remove: podman stop web && podman rm web
  • Inspect a running container: podman inspect web
  • Execute a command inside: podman exec -it web sh
  • List images: podman images
  • Prune stopped containers: podman container prune
  • Auto-update images: Add AutoUpdate=registry to the [Container] block, then enable podman-auto-update.timer with systemctl --user enable --now podman-auto-update.timer.

Troubleshooting

"permission denied" on volume mounts (SELinux hosts)

Always append :Z (private relabeling) or :z (shared relabeling) to volume flags on Fedora/RHEL. If the error persists, check ausearch -m avc -ts recent for AVC denials.

Container can't reach the internet

Rootless Podman uses slirp4netns or pasta for networking. Verify one is installed:

which slirp4netns pasta

Install the missing package (slirp4netns on Debian/Ubuntu/Fedora; passt on newer Podman defaults). Podman 5.x prefers pasta — install the passt package.

"rootlesskit" or namespace errors on Debian

Double-check /proc/sys/kernel/unprivileged_userns_clone is 1 (see the sysctl step above). Also confirm /etc/subuid and /etc/subgid have your user listed and re-run podman system migrate.

Quadlet unit file not picked up

Quadlet requires Podman 4.4+. Check your version with podman --version. On older installs, fall back to podman generate systemd --name web --files --new and copy the generated file to ~/.config/systemd/user/.

tested on:Ubuntu 24.04Debian 12Fedora 40Arch rolling

Frequently asked questions

Can I use Docker Compose files with rootless Podman?
Yes. Podman 4.7+ includes a native 'podman compose' subcommand, and the community podman-compose tool works with most Compose v3 files. Complex Compose features like build caching may behave slightly differently than with Docker.
Is rootless Podman less capable than running containers as root?
For most workloads, no. The main limitations are ports below 1024 (adjustable via sysctl) and some network modes that require CAP_NET_ADMIN. Applications that explicitly require root inside the container and on the host are rare and usually avoidable.
What is the difference between Quadlet and podman generate systemd?
podman generate systemd produces a static unit file snapshot tied to a specific container ID. Quadlet is declarative — you describe the desired container state and Podman regenerates the unit on each daemon-reload, making updates and image changes cleaner. Quadlet is the recommended approach on Podman 4.4+.
Does rootless Podman work inside a VM or WSL2?
Yes, with caveats. WSL2 supports user namespaces as of recent kernel versions. Nested virtualization (running containers in a VM that's already inside a hypervisor) works as long as the guest kernel exposes user namespace support. Check /proc/sys/kernel/unprivileged_userns_clone inside the VM.
How do I migrate from Docker to Podman without breaking existing scripts?
Podman ships a docker compatibility alias: on most distros, installing the podman-docker package creates a /usr/bin/docker symlink and a stub socket. Scripts calling 'docker' will transparently invoke Podman. Review any usage of docker.sock-based volume mounts, as those won't translate directly.

Related guides