Buildah is a Red Hat–sponsored command-line tool for building OCI-compliant container images without requiring a Docker daemon or root privileges. It ships in the default RHEL 8 AppStream repository alongside Podman, making it a natural fit for secure, rootless CI/CD pipelines. In this tutorial you will install Buildah and Podman on RHEL 8, build images from scratch and from a Dockerfile, push them to a container registry, and integrate rootless builds into a CI/CD workflow.

Prerequisites

  • RHEL 8 system with sudo privileges
  • A container registry account (Docker Hub, Quay.io, or a self-hosted Harbor instance)
  • Basic familiarity with container concepts (images, layers, entrypoints)

Step 1 — Install Buildah and Podman

Both tools are available directly from the RHEL 8 AppStream repository — no third-party repo required.

sudo dnf install -y buildah podman
buildah version
podman version

Verify that buildah and podman are both on your PATH. No daemon needs to be started — both tools operate directly against the kernel.

Step 2 — Build an Image from Scratch Using Buildah Commands

Buildah’s from subcommand starts a new working container. You can use scratch for a completely empty image or a base image such as ubi8 (Universal Base Image).

# Start a working container from UBI 8
container=$(buildah from registry.access.redhat.com/ubi8/ubi:latest)
echo "Working container: $container"

# Run a command inside the container to install packages
buildah run $container -- dnf install -y python3 curl && 
buildah run $container -- dnf clean all

# Copy a local application file into the container
echo 'print("Hello from Buildah!")' > app.py
buildah copy $container app.py /app/app.py

# Set environment variables and working directory
buildah config --env APP_ENV=production $container
buildah config --workingdir /app $container

# Set the default command (ENTRYPOINT + CMD)
buildah config --entrypoint '["python3"]' $container
buildah config --cmd '["/app/app.py"]' $container

# Inspect the configuration
buildah inspect $container | grep -A5 '"Entrypoint"'

Step 3 — Commit the Working Container to an Image

buildah commit snapshots the current state of the working container into a named OCI image stored in the local containers storage.

# Commit to a local image
buildah commit $container myapp:latest

# List local images (both buildah and podman share the same storage)
buildah images
podman images

# Run the new image with Podman to verify it works
podman run --rm myapp:latest

Step 4 — Build an Image from a Dockerfile with Buildah Bud

buildah bud (Build Using Dockerfile) is a drop-in replacement for docker build. It reads a standard Dockerfile and produces an OCI image without a daemon.

mkdir -p ~/myapp-docker && cd ~/myapp-docker

cat > Dockerfile << 'EOF'
FROM registry.access.redhat.com/ubi8/ubi:latest
RUN dnf install -y python3 && dnf clean all
WORKDIR /app
COPY app.py /app/app.py
ENV APP_ENV=production
ENTRYPOINT ["python3"]
CMD ["/app/app.py"]
EOF

cp ~/app.py .

# Build using buildah bud (rootless — no sudo required)
buildah bud -t myapp:dockerfile .

# Verify the image
podman run --rm myapp:dockerfile

Step 5 — Push the Image to a Container Registry

Use buildah push to upload the image to a registry. Credentials are stored securely in ~/.config/containers/auth.json — no root access required.

# Log in to the registry (credentials stored in ~/.config/containers/auth.json)
buildah login docker.io

# Tag the image for the registry
buildah tag myapp:latest docker.io/YOUR_USERNAME/myapp:latest

# Push the image
buildah push docker.io/YOUR_USERNAME/myapp:latest

# Alternatively, push to Quay.io
buildah login quay.io
buildah push myapp:latest quay.io/YOUR_USERNAME/myapp:latest

Step 6 — Rootless Builds in a CI/CD Pipeline

Because Buildah requires no daemon and no root privileges, it runs safely inside a CI/CD job without Docker socket mounting or privileged containers. The following example shows a minimal shell script suitable for a GitLab CI job or Drone pipeline step.

cat > build-and-push.sh << 'EOF'
#!/bin/bash
set -euo pipefail

IMAGE_NAME="myapp"
REGISTRY="docker.io/YOUR_USERNAME"
TAG="${CI_COMMIT_SHORT_SHA:-latest}"

echo "Building $IMAGE_NAME:$TAG"
buildah bud -t "${IMAGE_NAME}:${TAG}" .

echo "Pushing to ${REGISTRY}/${IMAGE_NAME}:${TAG}"
buildah push "${IMAGE_NAME}:${TAG}" "docker://${REGISTRY}/${IMAGE_NAME}:${TAG}"

echo "Tagging as latest"
buildah push "${IMAGE_NAME}:${TAG}" "docker://${REGISTRY}/${IMAGE_NAME}:latest"
EOF

chmod +x build-and-push.sh

# Run as a normal (non-root) user in CI
./build-and-push.sh

Conclusion

You have installed Buildah and Podman on RHEL 8 directly from the AppStream repository, built OCI images using both the low-level buildah from/run/copy/config/commit workflow and the familiar buildah bud Dockerfile approach, pushed images to a remote registry, and integrated rootless builds into a CI/CD script. The absence of a Docker daemon reduces the attack surface significantly and removes the need for privileged Docker socket mounts in CI environments, making Buildah the preferred image-building tool in modern RHEL 8 pipelines.

Next steps: Setting Up a Rootless Podman Systemd Service on RHEL 8, Scanning Container Images for Vulnerabilities with Trivy on RHEL 8, and Using Skopeo to Copy and Inspect Images Across Registries on RHEL 8.