The Problem with Managing Scattered Containers
When I first started using Podman, I would run each container separately: database on its own, backend on its own, frontend on its own. That approach was fine for quick testing, but within a few days the headaches became obvious: which container needs to start first? If the database isn’t up yet when the backend starts, it crashes immediately. Stopping everything at once meant stopping each one individually. Networking between containers required manually configuring a bridge and remembering internal DNS names.
After reading the Kubernetes documentation, I finally understood why they introduced the concept of a Pod — grouping related containers into a single unit. Podman supports Pods natively from the start, with no extra plugins or daemons required. This is also why many people switch from Docker to Podman when they start learning Kubernetes in a local environment.
Why Pods Solve This Problem
In Kubernetes architecture, a Pod is the smallest deployable unit — not an individual container. Containers within the same Pod share:
- The same network namespace — same IP, same loopback interface
- Communication over
localhostwithout exposing ports externally - Start and stop together as a unified block
- An
infra container(pause container) that holds the namespace — exactly like Kubernetes
Docker Compose links containers through a virtual network and internal DNS — that works fine, but it’s an abstraction specific to Compose, not the Kubernetes model. Podman Pod uses the same mechanism that Kubernetes uses, so when you read Kubernetes documentation later, the networking and Pod lifecycle concepts won’t feel foreign anymore.
Verify Podman Is Ready
I’ve been using Fedora as my main development machine for 2 years and I’m quite happy with how quickly packages are updated — Podman typically gets the latest version as soon as it’s released upstream. Check the current version:
podman --version
podman info | grep -E "version|rootless"
If it’s not installed or needs updating:
sudo dnf install -y podman
This guide applies to Podman 4.x and above. Fedora 38+ typically ships with Podman 4.x or 5.x in the default repository.
Real-world Example: WordPress + MariaDB in a Single Pod
Enough theory. Let me jump straight to a concrete example: running WordPress and MariaDB together in a single Pod. This use case is simple enough to set up in a few minutes, but demonstrates clearly what problems Pods solve compared to running standalone containers.
Step 1 — Create the Pod and Declare Ports
podman pod create \
--name wordpress-pod \
--publish 8080:80
Important: Port mappings must be declared when creating the Pod, not when creating containers. Since containers in a Pod share the Pod’s network namespace, only the Pod itself is the external port exposure point — individual containers cannot expose ports on their own.
Step 2 — Add MariaDB to the Pod
podman run -d \
--pod wordpress-pod \
--name mariadb \
-e MYSQL_ROOT_PASSWORD=rootpass \
-e MYSQL_DATABASE=wordpress \
-e MYSQL_USER=wpuser \
-e MYSQL_PASSWORD=wppass \
-v mariadb-data:/var/lib/mysql \
docker.io/library/mariadb:10.11
Step 3 — Add WordPress to the Pod
podman run -d \
--pod wordpress-pod \
--name wordpress \
-e WORDPRESS_DB_HOST=127.0.0.1:3306 \
-e WORDPRESS_DB_USER=wpuser \
-e WORDPRESS_DB_PASSWORD=wppass \
-e WORDPRESS_DB_NAME=wordpress \
-v wordpress-data:/var/www/html \
docker.io/library/wordpress:6.5
Notice WORDPRESS_DB_HOST=127.0.0.1 — because WordPress and MariaDB are in the same Pod, they communicate over localhost completely naturally, with no service discovery or internal DNS configuration needed like you’d require with a separate Docker network.
Step 4 — Inspect and Manage the Pod
# List all Pods
podman pod ps
# List containers with their Pod
podman ps --pod
# View logs for individual containers
podman logs wordpress
podman logs mariadb
Open your browser to http://localhost:8080 — if the WordPress setup screen appears, everything is working. Now try managing the Pod as a single unit:
# Stop the entire Pod
podman pod stop wordpress-pod
# Start it again
podman pod start wordpress-pod
# Restart
podman pod restart wordpress-pod
# Remove the Pod and all its containers
podman pod rm -f wordpress-pod
This is what I like most: a single command controls the entire stack, no need to remember individual container names or the order to stop them.
Automation with podman generate systemd
Running Pods manually is fine for testing, but when you need a service that automatically restarts after a reboot, you need systemd. podman generate systemd generates the unit files for you — no need to write them by hand.
Generate systemd Unit Files from an Existing Pod
# Navigate to the directory where you want to save the unit files
mkdir -p ~/.config/systemd/user/
cd ~/.config/systemd/user/
# Generate unit files
podman generate systemd \
--name \
--files \
--new \
wordpress-pod
Explanation of the flags:
--name: use container/pod names instead of IDs (easier to read in logs)--files: write output to files instead of printing to stdout--new: each start will runpodman runfrom scratch instead of attaching to an existing container — important for picking up new image updates
This command generates 3 files:
pod-wordpress-pod.service— manages the Pod and orchestrates its containerscontainer-mariadb.service— starts after the Pod is readycontainer-wordpress.service— starts after mariadb
Enable Rootless Services (Running as a Regular User)
# Reload systemd to pick up the new unit files
systemctl --user daemon-reload
# Enable the service to auto-start
systemctl --user enable pod-wordpress-pod.service
# Start it now
systemctl --user start pod-wordpress-pod.service
# Check status
systemctl --user status pod-wordpress-pod.service
Allow the Service to Run Without Being Logged In (Linger)
By default, user systemd services only run while you’re logged in. To have the Pod auto-start immediately after boot without needing to SSH in first:
sudo loginctl enable-linger $USER
# Verify
loginctl show-user $USER | grep Linger
After this command, the Pod behaves like a true system service while still running under your regular user account — far more secure than running as root.
The Best Approach: Rootless Pod + Linger + Persistent Volumes
After a lot of experimentation, this is the workflow I’ve settled on as the most stable for development projects on Fedora:
- Create named volumes first so data persists even when the Pod is removed and recreated
- Generate systemd with
--new— ensures containers are recreated from the latest image on every restart - Enable linger — no dependency on a login session
- Fully rootless — no
sudoneeded for any podman command
Example when a new WordPress version needs updating:
# Pull the new image
podman pull docker.io/library/wordpress:6.5
# Restart — with --new, systemd will automatically recreate the container from the new image
systemctl --user restart pod-wordpress-pod.service
Notes for Fedora 40+ and Podman 5.x
Since Podman 4.4, the podman generate systemd command has been marked deprecated — on Fedora 40+ with Podman 5.x, this warning appears more prominently. The official replacement is Quadlet: you write a .container or .pod file, and podman-system-generator automatically converts it to a systemd unit. Quadlet is less verbose, easier to version control, and is the approach Podman officially recommends going forward.
That said, podman generate systemd still works well and is the fastest way to understand the underlying mechanism before moving to Quadlet. Getting comfortable with generate systemd first will make Quadlet files much easier to read — the underlying logic is essentially the same.
Wrapping Up
I originally started using Pods just to solve one small problem: stopping all containers at once. But the real benefits turned out to be elsewhere — communication over localhost with zero configuration, synchronized lifecycle management, and podman generate systemd handling auto-restart after reboots. Rootless, no sudo, no daemon. On Fedora, this is the cleanest and most secure container workflow I’ve found.
