Podman pods mirror the Kubernetes pod concept — a group of containers that share the same network namespace, IPC namespace, and optionally storage volumes. This makes it straightforward to move workloads between a local RHEL 9 machine and a Kubernetes cluster with minimal changes. In this tutorial you will create a pod containing an Nginx web server and a PHP-FPM sidecar, inspect the pod lifecycle, generate a Kubernetes-compatible YAML manifest, and create a persistent systemd service. All steps use the rootless Podman mode available by default on RHEL 9.
Prerequisites
- RHEL 9 with Podman installed (
dnf install -y podman) - A regular (non-root) user account for rootless operation
- Internet access to pull container images
- Basic familiarity with containers and Linux networking
Step 1 — Create a Pod
A Podman pod is a shared network sandbox. You publish ports on the pod, not on individual containers. The pod automatically starts an infra container (pause image) that holds the network namespace:
# Create the pod and publish host port 8080 to pod port 80
podman pod create
--name mypod
-p 8080:80
--hostname mypod.local
# Verify the pod was created
podman pod ps
Expected output shows the pod in Created status with one container (the infra container). The pod ID is the canonical reference for lifecycle operations.
Step 2 — Add Containers to the Pod
Join containers to the pod using --pod. They share the loopback interface, so PHP-FPM is reachable by Nginx at 127.0.0.1:9000:
# Start Nginx inside the pod
podman run -d
--pod mypod
--name nginx
-v ./nginx.conf:/etc/nginx/conf.d/default.conf:ro,z
docker.io/library/nginx:latest
# Start PHP-FPM inside the same pod
podman run -d
--pod mypod
--name phpfpm
-v ./app:/var/www/html:ro,z
docker.io/library/php:8.2-fpm
# Confirm both containers are running inside the pod
podman ps --pod
The :z volume flag sets the correct SELinux label — always include it for bind mounts on RHEL 9. The podman ps --pod output shows each container’s associated pod ID and name.
Step 3 — Inspect and Manage the Pod Lifecycle
# List pods with status and container count
podman pod ps
# Show detailed pod info (IP, ports, all containers)
podman pod inspect mypod
# View logs from a specific container in the pod
podman logs nginx
# Execute a command inside a pod container
podman exec -it nginx bash
# Stop all containers in the pod at once
podman pod stop mypod
# Start the pod again
podman pod start mypod
# Remove the pod and all its containers (must stop first)
podman pod stop mypod
podman pod rm mypod
Step 4 — Generate a Kubernetes YAML Manifest
Podman can export a running pod as a Kubernetes-compatible Pod manifest. This YAML can be applied directly to a Kubernetes cluster or used to recreate the pod on another Podman host:
# Generate Kubernetes YAML from the running pod
podman generate kube mypod > mypod.yaml
# Inspect the generated manifest
cat mypod.yaml
# On another host (or after removing the local pod), recreate it:
podman play kube mypod.yaml
# Tear down what was created from the YAML:
podman play kube --down mypod.yaml
The generated YAML includes the container spec, volume mounts, and port mappings. You can push this file directly to a Kubernetes cluster with kubectl apply -f mypod.yaml.
Step 5 — Create a Systemd Service for the Pod
To start the pod at boot under your user account, generate systemd unit files and enable them. Rootless systemd services require lingering to be enabled so they start without an active login session:
# Ensure the pod is running before generating units
podman pod start mypod
# Generate systemd unit files (stored in user's systemd directory)
mkdir -p ~/.config/systemd/user
cd ~/.config/systemd/user
podman generate systemd
--name
--files
--new
mypod
# This creates:
# pod-mypod.service <- manages the pod
# container-nginx.service <- manages the nginx container
# container-phpfpm.service <- manages the phpfpm container
# Reload systemd user daemon and enable the pod service
systemctl --user daemon-reload
systemctl --user enable --now pod-mypod.service
# Enable lingering so the service starts at boot without a login
loginctl enable-linger $USER
# Check status
systemctl --user status pod-mypod.service
Step 6 — Run a Pod from Kubernetes YAML at Boot
Alternatively, use podman play kube as the service command for a cleaner Kubernetes-style workflow:
# Create a dedicated systemd unit
cat > ~/.config/systemd/user/mypod-kube.service << 'EOF'
[Unit]
Description=My Pod (Kubernetes YAML)
After=network-online.target
Wants=network-online.target
[Service]
Type=forking
Restart=on-failure
RestartSec=5
ExecStart=/usr/bin/podman play kube /home/%u/mypod.yaml
ExecStop=/usr/bin/podman play kube --down /home/%u/mypod.yaml
[Install]
WantedBy=default.target
EOF
systemctl --user daemon-reload
systemctl --user enable --now mypod-kube.service
Conclusion
Podman pods on RHEL 9 give you a lightweight, daemonless way to group containers, manage their shared network, and generate Kubernetes manifests without needing a cluster. The rootless systemd integration means you can run production workloads as an unprivileged user, reducing the attack surface. The workflow — create pod, add containers, export YAML, create systemd unit — maps directly to a Kubernetes deployment pipeline.
Next steps: How to Use Podman Volumes and Bind Mounts on RHEL 9, How to Build Container Images with Buildah on RHEL 9, and How to Deploy Podman Pods to OpenShift from RHEL 9.