How to Install Buildah and Podman on Rocky Linux 10 (Rootless Containers & SELinux)

uildah podman rocky linux 10, install podman rocky linux

If you followed our Docker on Rocky Linux 10 guide, you already know Docker runs fine on Rocky — but it isn’t the RHEL-native way to build and run containers. Red Hat’s own toolchain is Buildah (build container images) and Podman (run them), both daemonless, rootless-by-default, and deeply integrated with SELinux and systemd. This guide walks through installing both on Rocky Linux 10, building an image with Buildah, running it rootless with Podman, wiring it into systemd with Quadlet, and the SELinux gotchas you’ll hit along the way — following the same structure as our other Rocky Linux 10 guides.

Table of Contents

  1. Why Buildah + Podman Instead of Docker on Rocky Linux
  2. Buildah vs Podman vs Docker: Comparison
  3. Architecture Overview
  4. Prerequisites
  5. Installing Podman and Buildah
  6. Building an Image with Buildah
  7. Running Containers with Podman (Rootless)
  8. SELinux and Rootless Networking Considerations
  9. Podman Compose for Multi-Container Apps
  10. Running Podman as a systemd Service with Quadlet
  11. Full Command Reference Table
  12. Troubleshooting
  13. FAQ
  14. Related Articles

1. Why Buildah + Podman Instead of Docker on Rocky Linux

Podman and Buildah are developed by Red Hat and ship in Rocky Linux’s default (AppStream) repositories — no third-party repo needed, unlike Docker CE. They’re built around three ideas that matter more on an RPM/enterprise distro than on Ubuntu:

  • Daemonless. There’s no background dockerd process running as root. Each podman command runs as a direct child process, which reduces attack surface and simplifies systemd integration.
  • Rootless by default. Unprivileged users can build and run containers without ever touching the root-owned Docker socket — a meaningful security win in shared or regulated environments.
  • SELinux-native. Podman and Buildah were designed alongside SELinux, so container isolation is enforced by policy, not just namespaces.

If your portfolio goal (as covered in our Rocky Linux vs Ubuntu Cheat Sheet) is demonstrating RHEL-ecosystem fluency, Podman/Buildah is the expected toolchain — many RHEL shops actively avoid Docker CE in favor of the Red Hat-native stack.

2. Buildah vs Podman vs Docker: Comparison

FeatureDockerPodmanBuildah
ArchitectureClient-server (daemon)DaemonlessDaemonless
Root requirementDaemon runs as rootRootless by defaultRootless by default
Default in Rocky Linux reposNo (needs docker-ce repo)Yes (AppStream)Yes (AppStream)
Primary purposeBuild + runRun (+ can build)Build only (specialized)
systemd integrationVia docker.serviceNative (Quadlet)N/A (build tool)
Compose supportdocker compose (native)podman-compose / podman composeN/A
Image format compatibilityOCI / DockerOCI / DockerOCI / Docker
Typical use caseCloud-native, CI/CD, dev workstationsRHEL-family servers, security-sensitive workloadsCI pipelines building images without a daemon

In practice: Podman can both build and run images (podman build works fine), but Buildah gives finer-grained control over the build process — layer-by-layer scripting — which is why the two are often paired: Buildah for building, Podman for running.

3. Architecture Overview

   Docker model (daemon-based)              Podman/Buildah model (daemonless)
─────────────────────────── ─────────────────────────────────

┌───────────┐ ┌───────────┐
│ docker CLI │ │ podman CLI │
└─────┬──────┘ └─────┬──────┘
│ talks to socket (root) │ direct fork/exec
▼ ▼
┌───────────────┐ ┌───────────────────┐
│ dockerd daemon │ (runs as root) │ conmon (per- │
│ (always-on) │ │ container monitor)│
└───────┬────────┘ └─────────┬──────────┘
│ │
▼ ▼
┌───────────────┐ ┌───────────────────┐
│ containerd │ │ runc / crun │
└───────┬────────┘ │ (OCI runtime) │
▼ └─────────┬──────────┘
┌───────────────┐ ▼
│ runc │ Container process runs
└───────────────┘ as the invoking user
(rootless, no daemon)

Because there’s no long-running daemon, each Podman container is a direct child of the process that started it — which is also why Podman integrates naturally with systemd (via Quadlet, covered in Section 10) instead of needing a separate daemon to survive reboots.

4. Prerequisites

  • Rocky Linux 10 (minimal or server install), updated: sudo dnf update -y
  • A non-root user for rootless container work (recommended over using root directly)
  • SELinux in Enforcing mode (default) — this guide assumes it, per our Docker on Rocky Linux 10 guide
  • Basic firewalld familiarity if you’ll expose container ports externally (see our Rocky Linux vs Ubuntu Cheat Sheet, Section 5)

5. Installing Podman and Buildah

Both packages are in Rocky Linux’s default AppStream repo — no dnf config-manager --add-repo needed, unlike Docker CE.

# Update system packages first
sudo dnf update -y

# Install Podman, Buildah, and Skopeo (image inspection/copy tool)
sudo dnf install -y podman buildah skopeo

# Verify versions
podman --version
buildah --version

Expected output (versions will track whatever ships with your Rocky Linux 10 point release):

podman version 5.x.x
buildah version 1.3x.x

Confirm rootless mode works for your current user:

podman info --format '{{.Host.Security.Rootless}}'
# Expected: true

6. Building an Image with Buildah

Buildah supports both Dockerfile-based builds and its own scripted, layer-by-layer approach. Start with the familiar Dockerfile method:

# Dockerfile
FROM rockylinux:10-minimal
RUN microdnf install -y nginx && microdnf clean all
COPY index.html /usr/share/nginx/html/index.html
EXPOSE 8080
CMD ["nginx", "-g", "daemon off;"]
# Build with Buildah (Dockerfile-compatible)
buildah bud -t my-nginx:latest .

# Alternative: buildah's native scripted build (no Dockerfile needed)
container=$(buildah from rockylinux:10-minimal)
buildah run "$container" microdnf install -y nginx
buildah copy "$container" index.html /usr/share/nginx/html/index.html
buildah config --port 8080 --cmd "nginx -g 'daemon off;'" "$container"
buildah commit "$container" my-nginx:latest
buildah rm "$container"

The scripted approach is useful in CI pipelines where you want to conditionally add layers without maintaining multiple Dockerfiles.

7. Running Containers with Podman (Rootless)

# Run the image built above, rootless, unprivileged port
podman run -d --name web -p 8080:8080 my-nginx:latest

# Check running containers
podman ps

# View logs
podman logs -f web

# Stop / remove
podman stop web
podman rm web

Note: rootless Podman cannot bind to privileged ports (<1024) by default. Map to an unprivileged host port (like 8080 above) instead of 80, or adjust net.ipv4.ip_unprivileged_port_start if you specifically need a low port for a rootless container.

8. SELinux and Rootless Networking Considerations

Just like Docker on Rocky Linux (see our Docker + SELinux guide), bind-mounted volumes need an SELinux context label or the container will get Permission denied even though the file permissions look correct:

# :Z = private label (only this container can access)
podman run -d --name web -p 8080:8080 -v /data/html:/usr/share/nginx/html:Z my-nginx:latest

# :z = shared label (multiple containers can access the same volume)
podman run -d --name web -p 8080:8080 -v /data/html:/usr/share/nginx/html:z my-nginx:latest

Check SELinux denials the same way as with Docker:

sudo ausearch -m avc -ts recent | grep podman

Rootless networking note: rootless Podman containers, by default, run in a separate network namespace via slirp4netns or pasta (Podman 5+ defaults to pasta for better performance). This means rootless containers are NOT directly visible on the host’s bridge network the way rootful Docker containers are — published ports (-p) still work as expected, but if you need containers to be reachable exactly like on a standard bridge, consider rootful Podman (sudo podman ...) for that specific workload, or explicitly configure pasta/slirp4netns options.

9. Podman Compose for Multi-Container Apps

# Install podman-compose (Python-based, from EPEL)
sudo dnf install -y epel-release
sudo dnf install -y podman-compose
# docker-compose.yml (podman-compose reads the same format)
services:
  web:
    image: my-nginx:latest
    ports:
      - "8080:8080"
    volumes:
      - ./html:/usr/share/nginx/html:Z

  redis:
    image: docker.io/library/redis:7
    ports:
      - "6379:6379"
podman-compose up -d
podman-compose ps
podman-compose down

Newer Podman versions also support podman compose (no hyphen) as a built-in subcommand if the podman-plugins package or a compatible compose provider is installed — check podman compose version to see which backend is active.

10. Running Podman as a systemd Service with Quadlet

Quadlet is Podman’s native way to define containers as systemd units, replacing the older podman generate systemd approach. This is the idiomatic way to make a container start on boot on Rocky Linux.

# Create the Quadlet directory for a rootless user
mkdir -p ~/.config/containers/systemd
# ~/.config/containers/systemd/my-nginx.container
[Unit]
Description=My Nginx container (Podman Quadlet)
After=network-online.target

[Container]
Image=localhost/my-nginx:latest
PublishPort=8080:8080
Volume=%h/data/html:/usr/share/nginx/html:Z

[Service]
Restart=always

[Install]
WantedBy=default.target
# Reload systemd (user scope) and start
systemctl --user daemon-reload
systemctl --user enable --now my-nginx.service

# Check status like any systemd unit
systemctl --user status my-nginx.service

# Allow the rootless service to keep running after logout
loginctl enable-linger "$(whoami)"

11. Full Command Reference Table

TaskDockerPodman / Buildah
Build image (Dockerfile)docker build -t img .buildah bud -t img . or podman build -t img .
Run containerdocker run -d -p 80:80 imgpodman run -d -p 8080:8080 img
List running containersdocker pspodman ps
View logsdocker logs -f <ctr>podman logs -f <ctr>
Stop / remove containerdocker stop/rm <ctr>podman stop/rm <ctr>
List imagesdocker imagespodman images
Pull imagedocker pull <img>podman pull <img>
Inspect image without pulling— (needs docker pull first)skopeo inspect docker://<img>
Compose updocker compose up -dpodman-compose up -d or podman compose up -d
Run on boot (systemd)docker.service (daemon)Quadlet .container unit (per-container)
Rootless by defaultNoYes

12. Troubleshooting

SymptomLikely CauseFix
permission denied on bind-mounted volumeMissing SELinux context labelAdd :Z (private) or :z (shared) to the volume mount
podman run -p 80:80 fails with “permission denied”Rootless container can’t bind privileged port (<1024)Use a port ≥1024 (e.g., 8080), or lower net.ipv4.ip_unprivileged_port_start
Container unreachable from another host despite -pfirewalld blocking the portfirewall-cmd --permanent --add-port=8080/tcp && firewall-cmd --reload
podman-compose: command not foundEPEL not enabledsudo dnf install -y epel-release && sudo dnf install -y podman-compose
Quadlet .container unit not picked upWrong directory or missing daemon-reloadConfirm file is in ~/.config/containers/systemd/, run systemctl --user daemon-reload
Rootless service stops after logoutLingering not enabled for the userloginctl enable-linger $(whoami)
buildah bud can’t resolve base imageNo network access or registry misconfiguredCheck /etc/containers/registries.conf, confirm outbound DNS/network
Container loses network reachability intermittently (rootless)slirp4netns/pasta networking limitationConsider rootful Podman (sudo podman) for that workload, or review pasta network options

13. FAQ

Do I need Docker installed alongside Podman?
No. Podman and Buildah are fully independent of Docker — they read the same Dockerfile syntax and OCI image format, so existing Dockerfiles work unmodified.

Can Podman run Docker Compose files directly?
Yes, via podman-compose or the podman compose subcommand — both read standard docker-compose.yml syntax without modification, aside from occasionally needing SELinux :Z/:z suffixes added to volumes.

Is rootless Podman actually more secure than rootful Docker?
Rootless mode significantly reduces the blast radius of a container escape, since the container process runs with the invoking user’s privileges rather than root — but it isn’t a complete substitute for other hardening (image scanning, SELinux, least-privilege). Treat it as one layer of defense-in-depth, not the whole strategy.

Why does Red Hat prefer Podman over Docker?
Podman’s daemonless, rootless design aligns with RHEL’s general security posture (fewer always-on root processes) and its systemd-first service model — which is why Podman/Buildah, not Docker CE, ship in Rocky Linux’s default repositories.

Does Buildah replace Podman?
No, they’re complementary. Buildah specializes in building images with fine-grained control; Podman specializes in running and managing containers day to day. Many workflows use both together.

(Visited 1 times, 1 visits today)

You may also like