Deploying Podman Compose on CentOS Stream 9: A Rootless and SELinux Solution to Replace Docker

CentOS tutorial - IT technology blog
CentOS tutorial - IT technology blog

From the CentOS 8 EOL Shock to the Transition to Podman

When CentOS 8 suddenly reached End of Life (EOL), I spent many sleepless nights migrating five production server clusters to CentOS Stream 9. During that process, I realized Docker was no longer the top priority in the Red Hat ecosystem. Instead, Podman was deeply integrated and became the default tool. Initially, the absence of docker-compose made me quite hesitant. However, after mastering Podman Compose and the Rootless mechanism, I found the system to be not only lighter but significantly more secure.

Real-world Comparison: Docker Compose vs. Podman Compose

The biggest difference lies in the operational architecture. Docker relies on a Daemon running with root privileges. If a hacker gains control of the Daemon, they gain full control of your server. In contrast, Podman operates on a daemonless model. Each container is simply a standard user process.

Key Advantages of Podman Compose:

  • Rootless Security: You can run the entire application stack without sudo. This reduces the risk of privilege escalation from containers by 90%.
  • Resource Efficiency: Removing the Daemon reduces static RAM consumption by about 50MB – 100MB per node.
  • SELinux Compatibility: Podman automatically handles security labels, which are often a “nightmare” when using Docker on CentOS.

Why Podman is the Top Choice for CentOS Stream 9?

On CentOS Stream 9, SELinux is always in Enforcing mode. Docker frequently faces file access issues when mounting volumes, forcing users to disable SELinux—an extremely risky move. Podman is the opposite. It is designed to understand and work seamlessly with Red Hat’s security policies. Instead of breaking the protection layer, Podman leverages it to better isolate applications.

Detailed Deployment Guide

1. Installing Podman and Supporting Tools

First, update your system and install Podman along with Python Pip. On CentOS Stream 9, these packages are readily available in the official repositories:

sudo dnf update -y
sudo dnf install podman python3-pip -y

Next, install podman-compose via pip to get the latest version:

pip3 install podman-compose --user

Enable environment variables so the system recognizes the command:

echo 'export PATH=$PATH:$HOME/.local/bin' >> ~/.bashrc
source ~/.bashrc

2. Configuring Rootless Privileges for Users

To run containers without root, a user needs a specific range of IDs for identification within the system. Check the following configuration files:

cat /etc/subuid
cat /etc/subgid

If the files are empty, assign an ID range (typically 65,536 IDs) to the user with the following command:

sudo usermod --add-subuids 100000-165535 --add-subgids 100000-165535 $USER

3. Solving the SELinux Challenge with Volume Mounts

This is the most common error: the container reports “Permission Denied” even if you’ve used chmod 777. The cause is that the host directory’s SELinux label doesn’t match the container. In your docker-compose.yaml file, simply append the :Z suffix to the volume path.

Example configuration for running a secure Web Server:

version: '3.8'
services:
  db:
    image: mariadb:10.6
    environment:
      MYSQL_ROOT_PASSWORD: strong_password_here
    volumes:
      - ./db_data:/var/lib/mysql:Z # :Z helps relabel to container_file_t

  web:
    image: nginx:alpine
    ports:
      - "8080:80"
    volumes:
      - ./html:/usr/share/nginx/html:Z
    depends_on:
      - db

4. Managing Applications

Start the entire stack with the familiar command:

podman-compose up -d

To check the status of containers running under the current user:

podman ps

Tips for Handling Low Ports (80, 443)

In rootless mode, you cannot directly bind to ports below 1024. Instead of running as root, I usually bind the application to a high port (like 8080) and then use firewalld to redirect traffic. This approach ensures security while meeting strict compliance standards.

Command to forward port 80 to 8080:

sudo firewall-cmd --add-forward-port=port=80:proto=tcp:toport=8080 --permanent
sudo firewall-cmd --reload

Switching from Docker to Podman might take some time to get used to initially. However, the stability and security it brings to CentOS Stream 9 are well worth it. Good luck with your configuration!

Share: