What Is Podman and How Does It Compare to Docker on Fedora?

Podman is a daemonless, rootless container engine for Fedora that offers Docker compatibility without a background service.

The scenario

You just switched from a Docker-heavy workflow to Fedora. You type docker run out of habit and get a command not found error. You install Docker from the official repository, but the package manager warns about conflicting dependencies. You read a forum thread that says "just use Podman" but gives zero context about why Fedora made that choice. You need to understand the architecture shift, run your containers safely without sudo, and figure out how to keep them alive across reboots.

What is actually happening under the hood

Docker relies on a central background daemon. Every container command you type sends a request to that daemon over a Unix socket. The daemon runs as root, manages the container lifecycle, and holds the keys to the storage driver. That architecture works fine for CI servers and cloud clusters. It creates a single point of failure and forces every container to inherit root privileges unless you carefully configure user namespaces.

Podman removes the daemon. The podman binary forks a child process for each container. That child process runs with the exact same privileges as your user account. If you run a container without sudo, the container runs without root. The process tree looks like a standard Linux application instead of a nested service hierarchy.

Think of Docker like a hotel concierge. You hand over your request, the concierge talks to the front desk, the front desk talks to housekeeping, and eventually your room gets cleaned. Podman is like carrying your own keycard. You walk directly to the room, open the door, and do the work yourself. There is no middleman to crash, no socket to time out, and no elevated privilege to leak.

Fedora ships Podman by default because it aligns with the distribution security model. Rootless containers keep your user namespace isolated from the host. The crun runtime handles the low-level container creation using Linux namespaces and cgroups. Podman also speaks the OCI standard, which means images pulled from Docker Hub work without conversion. The CLI is deliberately compatible. You can usually swap docker for podman in existing scripts and they will run.

The storage backend operates differently too. Docker stores images in /var/lib/docker and requires root access to modify the overlay filesystem. Podman stores images in ~/.local/share/containers/storage for rootless users. The first run initializes the directory structure and configures the overlay driver. You do not need to tweak storage drivers unless you hit a specific permission error. Fedora convention aside: always check journalctl -xe when a service fails to start. The x flag adds explanatory text and the e flag jumps to the end. Most sysadmins type journalctl -xeu <unit> muscle-memory style. The same discipline applies to container logs.

How to run your first container

Start by pulling an image and running it interactively. This command fetches the Alpine Linux image, starts a shell, and removes the container when you exit.

podman run --rm -it docker.io/library/alpine:latest /bin/sh
# --rm cleans up the container filesystem immediately after exit
# -it allocates a pseudo-TTY and keeps stdin open for interactive use
# docker.io/library/ is the default registry prefix for public images
# /bin/sh is the entrypoint command that runs inside the container

Notice the absence of sudo. If you are on a default Fedora Workstation or Server install, your user already has access to the rootless storage backend. The first run will initialize the ~/.local/share/containers directory. Podman uses fuse-overlayfs or overlay depending on your kernel version and user namespace configuration. You do not need to tweak storage drivers unless you hit a specific permission error.

Run a detached service container to see how background processes behave. This example starts a lightweight HTTP server and leaves it running.

podman run -d --name test-web -p 8080:80 docker.io/library/alpine:latest nc -l -p 80 -e /bin/cat
# -d detaches the container so it runs in the background
# --name assigns a human-readable identifier for later management
# -p maps host port 8080 to container port 80 for network access
# nc listens on port 80 inside the container and echoes received data

Fedora convention aside: always check systemctl status before restarting services. With Podman, you check podman ps and podman logs <container> instead. The philosophy remains the same. Inspect the state before forcing a restart.

Verify it worked

Confirm the container is running and inspect its resource limits. This command lists active containers with their status, port mappings, and uptime.

podman ps -a --format "table {{.Names}}\t{{.Status}}\t{{.Ports}}"
# -a includes stopped containers in the output
# --format overrides the default table layout to show only relevant columns
# {{.Names}} and {{.Status}} pull fields from the container metadata

Test the network mapping from the host. This curl request verifies that the port forward is active and the container is responding.

curl -s http://localhost:8080/test
# -s suppresses progress output so only the response body prints
# localhost:8080 targets the host port mapped to the container
# /test is the payload sent to the netcat listener inside

Check the storage backend to ensure rootless isolation is working correctly. This command shows where Podman keeps images and container layers.

podman info --format "{{.Store.GraphRoot}}"
# --format extracts a single field from the JSON configuration dump
# GraphRoot points to the directory holding the overlay filesystem
# Rootless installs default to ~/.local/share/containers/storage

Reboot before you debug. Half the time the symptom is gone after a clean namespace reset.

Systemd integration and persistent services

Containers started with podman run die when you close the terminal or reboot the machine. Production workloads need automatic restarts, dependency ordering, and clean shutdown signals. Podman solves this with Quadlet files. Quadlet is a systemd unit parser that generates podman run commands at boot time. You write a simple .container file in ~/.config/containers/systemd/ or /etc/containers/systemd/, and systemd handles the lifecycle.

Create a persistent web service using this Quadlet definition. The file replaces the need for podman generate systemd or manual wrapper scripts.

[Unit]
Description=Persistent Alpine Web Server
After=network-online.target
# After ensures the network stack is fully initialized before starting
# network-online.target waits for DHCP and routing to settle

[Container]
Image=docker.io/library/alpine:latest
Exec=nc -l -p 80 -e /bin/cat
PublishPort=8080:80
# Image specifies the OCI image to pull and run
# Exec replaces the default entrypoint with a custom command
# PublishPort maps host and container ports without CLI flags

[Install]
WantedBy=default.target
# WantedBy controls which systemd target pulls this unit at boot
# default.target is the standard multi-user runlevel on Fedora

Place the file in ~/.config/containers/systemd/web.container. Reload the systemd user manager to pick up the new unit.

systemctl --user daemon-reload
# daemon-reload scans the unit directories for new or modified files
# --user targets the user instance instead of the system manager
# This step is required after every Quadlet file change

Start and enable the service. This command activates the container and registers it for automatic boot startup.

systemctl --user enable --now web.container
# enable creates the symlink in the wanted-by target directory
# --now starts the unit immediately after enabling
# systemd will now manage the container lifecycle across reboots

Check the service status to confirm systemd is tracking the container correctly.

systemctl --user status web.container
# status shows the active state, recent journal lines, and resource usage
# --user queries the user manager where rootless containers live
# Always verify the active state before assuming the service is healthy

Trust the package manager. Manual file edits drift, snapshots stay.

Common pitfalls and what the error looks like

The most frequent blocker is a missing user namespace range. Fedora Workstation enables user namespaces by default, but minimal server installs or custom cloud images sometimes leave the limit too low. If you see this error during podman run, your system needs a configuration tweak.

Error: crun: opening file `shiftfs` failed: No such file or directory

The error means the kernel cannot map your host user ID to a container user ID. Run this command to expand the range in your login session.

echo "100000 65536" | sudo tee -a /etc/subuid /etc/subgid
# 100000 is the starting UID/GID for rootless containers
# 65536 is the range size, allowing enough IDs for container processes
# tee -a appends the line to both files without overwriting existing entries

Log out and log back in for the new range to take effect. Podman reads these files during initialization.

Another common issue involves SELinux denials when mounting host directories. Podman expects the :z or :Z suffix on volume mounts to relabel the content. If you omit it, the container process gets denied access.

Error: container_linux.go:380: starting container process caused: process_linux.go:545: container init caused: rootfs_linux.go:75: mounting "/home/user/data" to rootfs at "/data" caused: "operation not permitted"

Fix the mount command by adding the SELinux label. This tells the security module to share the volume context across containers.

podman run -d --name data-test -v /home/user/data:/data:z docker.io/library/alpine:latest sleep 3600
# -v mounts the host directory into the container filesystem
# :z relabels the mount point for shared use across multiple containers
# sleep 3600 keeps the container alive long enough to test the mount

Fedora convention aside: config files in /etc/ are user-modified. Files in /usr/lib/ ship with the package. Edit /etc/. Never edit /usr/lib/. The same rule applies to container storage paths. Keep your overrides in ~/.config/containers/storage.conf instead of touching the system defaults.

Run journalctl first. Read the actual error before guessing.

When to use Podman versus Docker

Use Podman when you want rootless containers that run under your user account without a background daemon. Use Podman when you need systemd integration through Quadlet files for automatic container management. Use Podman when you are deploying on Fedora systems and want native package support without third-party repositories. Use Docker when you are maintaining legacy CI pipelines that depend on the Docker socket for build orchestration. Use Docker when your team relies on Docker Compose v2 with specific daemon-level networking features that Podman Compose has not yet replicated. Stay on the upstream Workstation defaults if you only run occasional development containers and do not need complex orchestration.

Where to go next