Podman Compose (podman-compose) is a Python implementation of the Docker Compose specification that uses Podman instead of Docker. It allows running existing docker-compose.yml / compose.yaml files with Podman’s rootless, daemon-less container engine. For teams migrating from Docker to Podman, podman-compose provides a familiar workflow with minimal configuration changes. Alternatively, Podman 4.0+ includes a native podman compose integration that delegates to an installed Compose provider (such as podman-compose or Docker Compose). This guide covers installing and using Podman Compose on RHEL 9, key differences from Docker Compose, and generating systemd services for production deployments of Compose stacks.
Prerequisites
- Podman installed on RHEL 9
Step 1 — Install Podman Compose
# Install from RHEL AppStream
dnf install -y podman-compose
# Or via pip (for the latest version)
pip3 install podman-compose
podman-compose version
Step 2 — Run an Existing Docker Compose File
mkdir /var/www/mystack && cd /var/www/mystack
# compose.yaml (same format as Docker Compose)
services:
web:
image: nginx:alpine
ports:
- "8080:80"
volumes:
- ./html:/usr/share/nginx/html:z # Note :z for SELinux on RHEL
depends_on:
- app
app:
image: node:20-alpine
command: node server.js
working_dir: /app
volumes:
- ./app:/app:z
environment:
- NODE_ENV=production
db:
image: postgres:16-alpine
environment:
POSTGRES_PASSWORD: ${DB_PASSWORD}
volumes:
- pgdata:/var/lib/postgresql/data:z
volumes:
pgdata:
# Run the stack with podman-compose
DB_PASSWORD=SecurePass123! podman-compose up -d
podman-compose ps
podman-compose logs -f
Step 3 — Key Differences from Docker Compose
# 1. SELinux volume labels are required on RHEL 9:
# Add :z (shared) or :Z (private) to all volume mounts
# Docker Compose: ./html:/usr/share/nginx/html
# Podman Compose: ./html:/usr/share/nginx/html:z
# 2. Rootless port binding — ports = 8080 or set:
sysctl -w net.ipv4.ip_unprivileged_port_start=80
# 3. No daemon — containers belong to the user who started them
# 4. Networking: uses slirp4netns for rootless networking by default
Step 4 — Generate systemd Services for Auto-Start
# Generate a systemd unit for the entire compose stack (Podman 4.4+)
podman generate systemd --name mystack --new --files
# Install and enable
mkdir -p ~/.config/systemd/user
cp *.service ~/.config/systemd/user/
systemctl --user daemon-reload
systemctl --user enable --now pod-mystack.service
# Enable auto-start without requiring user login
loginctl enable-linger $USER
Step 5 — Common Commands
podman-compose up -d # Start in detached mode
podman-compose down # Stop and remove containers
podman-compose down -v # Also remove volumes
podman-compose restart # Restart all services
podman-compose exec web sh # Shell into web container
podman-compose logs web # Logs for a specific service
podman-compose pull # Pull updated images
Conclusion
Podman Compose on RHEL 9 provides a Docker Compose-compatible workflow with Podman’s rootless security model. The two most important adaptations when migrating from Docker Compose are: adding SELinux labels (:z or :Z) to all bind mount volumes, and using ports above 1024 for rootless container deployments. For production environments requiring containers that survive system reboots without user login, use the generated systemd units with loginctl enable-linger to allow user-level systemd services to start at boot.
Next steps: How to Install Podman on RHEL 9, How to Install Docker Compose on RHEL 9, and How to Install Kubernetes on RHEL 9.