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.