You need containers but you do not want a root daemon
You just switched from a Windows or macOS workflow to Fedora and you need to run a web server, a database, or a development environment inside a container. You remember Docker, but the package manager suggests podman instead. You run the install command, try to start a container, and get a confusing permission error or a warning about a missing daemon. You want containers that just work, restart when you reboot, and do not require handing over root privileges to a background process.
How Podman replaces the Docker daemon
Docker relies on a central daemon that runs as root. Every container command talks to that daemon over a Unix socket. Podman flips that model. It uses a fork-exec architecture. Each container runs as a direct child process of your user account. There is no central daemon to manage. When you close your terminal, the containers keep running because systemd takes over the lifecycle. Fedora ships with user namespaces enabled by default, which means your user account gets its own isolated root inside the container. The container sees a root user, but that root maps to an unprivileged UID on the host. This keeps your host filesystem safe even if the container breaks out.
The tradeoff is that you need to tell systemd to manage the container socket for your user. Without that socket, background containers die when you log out, and auto-restart features stay dormant. The socket activation model means the Podman service only consumes memory when a command actually connects to it. Idle systems stay lean.
Install and enable the rootless socket
Install the core runtime and the supporting tools. Fedora keeps these in the default repositories. You do not need third-party sources or manual RPM downloads.
sudo dnf install podman buildah skopeo -y
# podman handles runtime and orchestration
# buildah builds images without needing a running container
# skopeo copies and inspects images across registries
# -y skips the confirmation prompt for a clean install
Enable the user-level systemd socket. This is the bridge that lets you run containers in the background and use features like podman auto-update. The --user flag tells systemd to manage this service under your home directory, not as a system-wide daemon.
systemctl --user enable --now podman.socket
# --user targets the user instance of systemd
# enable creates the symlink so it starts on login
# --now starts the socket immediately in the current session
# podman.socket activates the podman service on demand
If you rely on scripts that explicitly call docker run, install the compatibility shim. It translates Docker CLI syntax into Podman commands without changing your workflow.
sudo dnf install podman-docker -y
# replaces the docker binary with a symlink to podman
# translates docker-compose and docker CLI flags automatically
# keeps legacy scripts functional during migration
Reboot before you debug. Half the time the symptom is gone.
Configure storage and runtime paths
Podman stores container images, layers, and runtime state in a specific directory tree. Fedora uses overlay as the default storage driver. The configuration lives in two places. Files in /usr/lib/containers/ ship with the package and get overwritten on updates. Files in /etc/containers/ are user-modified and take precedence. Always edit /etc/. Never edit /usr/lib/.
sudo mkdir -p /etc/containers
# creates the user configuration directory if missing
# ensures your overrides survive package upgrades
# matches the standard Fedora configuration hierarchy
Check your current storage driver and graph root. The output confirms whether the overlay driver is active and where your container data lives.
podman info --format '{{.Store.GraphDriverName}}:{{.Store.GraphRoot}}'
# queries the runtime for storage configuration
# --format uses Go templates to extract specific fields
# prints the driver name and absolute path in one line
If you run out of space on your home partition, you can move the storage root to a larger disk. Edit /etc/containers/storage.conf and change the graphroot path. Restart the user socket afterward. The overlay driver requires a filesystem that supports user xattrs. Btrfs and XFS work out of the box. Ext4 requires the user_xattr mount option.
Run journalctl -xeu podman.socket to verify the service started cleanly. The x flag adds explanatory text and the e flag jumps to the end. Most sysadmins type this muscle-memory style when troubleshooting socket activation.
Verify the runtime and test a container
Check that the socket is active and listening. The output should show active (listening) and point to a socket path inside your ~/.config/systemd/user/ directory.
systemctl --user status podman.socket
# confirms the socket is bound and ready for connections
# shows the activation count to verify background usage
# displays the main PID if the service is currently running
Run a minimal test container. Podman defaults to rootless execution, so you drop sudo from every command. The --rm flag cleans up the container filesystem immediately after it exits.
podman run --rm hello-world
# pulls the image from the default registry if missing
# creates a temporary rootless container namespace
# executes the entrypoint and prints a success message
# --rm removes the container layer after exit
Run podman ps -a to see the container history. An empty list means the cleanup worked as expected. If you see a Permission denied error during the pull, your user namespace configuration is likely misaligned. Run podman system reset to clear corrupted state, then retry the socket enable command.
Common pitfalls and firewall boundaries
Port publishing fails silently if the firewall blocks the host port. Podman does not modify firewalld rules automatically. You must open the port manually and reload the runtime configuration.
sudo firewall-cmd --permanent --add-port=8080/tcp
# adds the rule to the persistent configuration file
# --permanent ensures the rule survives a reboot
# 8080/tcp matches the port you will publish in podman run
sudo firewall-cmd --reload
# applies the persistent rules to the active firewall session
# without this step the runtime config and persistent config diverge
SELinux denials appear when you mount host directories into containers with incorrect labels. Rootless Podman handles most labels automatically, but custom volume mounts sometimes trigger Permission denied errors. Check the audit log before changing security contexts.
sudo ausearch -m avc -ts recent
# filters the audit log for SELinux access vector cache denials
# -ts recent limits output to the last few minutes
# shows the exact source process and target file path
If the log shows a denial on a mounted directory, use chcon to apply the correct container file label. Do not disable SELinux. The label mismatch is a configuration issue, not a security flaw.
sudo chcon -Rt container_file_t /path/to/host/directory
# applies the container_file_t context recursively
# allows the container process to read and write the mount
# preserves the original SELinux policy enforcement
SELinux denials also show up in journalctl -t setroubleshoot with a one-line summary. Read those before touching setenforce. The troubleshoot daemon translates raw audit records into human-readable advice.
Trust the package manager. Manual file edits drift, snapshots stay.
Choose the right container toolchain
Use Podman when you want a daemonless, rootless runtime that integrates with systemd and user namespaces. Use Docker when you are running legacy enterprise tooling that requires the Docker API and a centralized daemon. Use Buildah when you need to construct container images in CI pipelines without pulling a base container. Use Skopeo when you only need to copy, inspect, or sign images across registries without running them. Stay on the native Podman CLI if you are writing new automation and want direct access to Kubernetes-compatible pod management.