How to Install and Use Podman with Buildah on RHEL 7

As enterprises move away from Docker and toward daemonless container tools, Podman and Buildah have emerged as the preferred container toolkit on Red Hat Enterprise Linux. Podman provides a Docker-compatible CLI for running containers without a background daemon, while Buildah specializes in constructing OCI-compliant images programmatically — without ever needing a Dockerfile. Together, these tools give RHEL 7 administrators fine-grained control over every layer in an image, from initial scratch to final push to a registry. This tutorial walks through installing both tools via EPEL and the extras repository, building a custom image from scratch using Buildah’s scripting API, and pushing the result to a remote registry.

Prerequisites

  • RHEL 7 with an active Red Hat subscription or CentOS 7 equivalent
  • Root or sudo privileges
  • EPEL repository enabled
  • At least 2 GB of free disk space in /var/lib/containers
  • A container registry account (Docker Hub, Quay.io, or a self-hosted Harbor instance) if you plan to push images

Step 1: Enable Repositories and Install Buildah and Podman

Buildah and Podman are available in the extras channel on RHEL 7 and through EPEL. First, confirm your subscriptions and enable the necessary repos:

# Enable the extras repo (RHEL 7 subscriptions)
sudo subscription-manager repos --enable=rhel-7-server-extras-rpms

# Alternatively, for CentOS 7, the extras repo is enabled by default.
# Install EPEL for supplementary packages:
sudo yum install -y epel-release

# Install Buildah and Podman
sudo yum install -y buildah podman

# Verify installations
buildah --version
podman --version

Expected output should show version strings such as buildah version 1.x.x and podman version 1.x.x. On RHEL 7, versions available via the extras channel may lag behind upstream, but they are tested and supported by Red Hat.

Step 2: Building a Container Image from Scratch with Buildah

One of Buildah’s most powerful features is the ability to build an image from absolute scratch — no base layer, no parent image. This is ideal for building minimal, security-hardened containers that contain only what is necessary.

# Start a new container from scratch (empty filesystem)
newcontainer=$(buildah from scratch)
echo "Container name: $newcontainer"

At this point, $newcontainer holds a working container handle — essentially a writable overlay over an empty base. You can now mount its filesystem and populate it:

# Mount the container's filesystem
scratchmnt=$(buildah mount $newcontainer)
echo "Mounted at: $scratchmnt"

# Install a minimal set of packages into the container's filesystem
# using the host's yum, targeting the mounted path
sudo yum install -y --installroot=$scratchmnt 
    --releasever=7 
    --setopt=tsflags=nodocs 
    bash coreutils glibc

# Clean yum cache inside the container
sudo yum clean all --installroot=$scratchmnt

Step 3: Running Commands Inside the Container with buildah run

The buildah run command executes a command inside the working container, similar to docker run --rm or a RUN instruction in a Dockerfile. This is where you install application packages, configure files, and set up runtime dependencies.

# Install additional packages inside the container using its own package manager
buildah run $newcontainer -- /bin/bash -c "yum install -y curl wget && yum clean all"

# Create an application directory
buildah run $newcontainer -- mkdir -p /app

# Check the running environment
buildah run $newcontainer -- uname -a
buildah run $newcontainer -- rpm -qa | head -20

Step 4: Copying Files into the Container with buildah copy

Use buildah copy to transfer files from the host into the working container. This replaces the COPY or ADD Dockerfile directive.

# Create a sample application script on the host
cat > /tmp/app.sh <<'EOF'
#!/bin/bash
echo "Application started at $(date)"
exec "$@"
EOF
chmod +x /tmp/app.sh

# Copy it into the container
buildah copy $newcontainer /tmp/app.sh /app/app.sh

# Copy an entire directory
# buildah copy $newcontainer ./myapp/ /app/

Step 5: Configuring the Image with buildah config

The buildah config command sets OCI image metadata — the equivalent of CMD, ENTRYPOINT, EXPOSE, ENV, and LABEL Dockerfile directives.

# Set the working directory
buildah config --workingdir /app $newcontainer

# Set an environment variable
buildah config --env APP_ENV=production $newcontainer

# Set the default command (CMD)
buildah config --cmd "/app/app.sh" $newcontainer

# Set the entrypoint
buildah config --entrypoint '["bash"]' $newcontainer

# Expose a port
buildah config --port 8080 $newcontainer

# Add image labels (OCI annotations)
buildah config 
    --label maintainer="[email protected]" 
    --label version="1.0.0" 
    --label description="Minimal RHEL 7 application image" 
    $newcontainer

# Verify the configuration
buildah inspect $newcontainer | python -c "import sys,json; d=json.load(sys.stdin); print(json.dumps(d['OCIv1']['config'], indent=2))"

Step 6: Committing the Image with buildah commit

Once you are satisfied with the working container’s state and configuration, commit it to produce a final image. The resulting image is OCI-compliant and can be used by Podman, Docker, or pushed to any standard registry.

# Commit the container to an image
buildah commit $newcontainer myapp:1.0.0

# List local images (Podman and Buildah share the same image store)
podman images
buildah images

# Tag the image for a remote registry
buildah tag myapp:1.0.0 quay.io/myorg/myapp:1.0.0

Step 7: Pushing the Image to a Registry

Buildah can push images directly to any OCI-compliant registry, including Docker Hub, Quay.io, or a self-hosted Harbor instance.

# Log in to the target registry
buildah login quay.io
# (Enter your username and password at the prompts)

# Push the image
buildah push quay.io/myorg/myapp:1.0.0

# Alternatively, use Podman to push (same credentials store):
podman login quay.io
podman push quay.io/myorg/myapp:1.0.0

# Unmount the scratch container (if still mounted)
buildah unmount $newcontainer

# Remove the working container
buildah rm $newcontainer

Step 8: Buildah vs. the Dockerfile Approach

For teams already using Dockerfiles, Buildah provides a buildah bud (build-using-dockerfile) command that builds OCI images from standard Dockerfile syntax without requiring a Docker daemon:

# Build using a standard Dockerfile
cat > /tmp/Dockerfile <<'EOF'
FROM registry.access.redhat.com/ubi7/ubi-minimal:latest
RUN microdnf install -y curl && microdnf clean all
WORKDIR /app
COPY app.sh /app/app.sh
RUN chmod +x /app/app.sh
EXPOSE 8080
CMD ["/app/app.sh"]
EOF

buildah bud -t myapp-dockerfile:1.0.0 -f /tmp/Dockerfile /tmp/

# The result is a standard OCI image:
podman run --rm myapp-dockerfile:1.0.0

The key difference is that buildah bud does not require root privileges or a running Docker daemon, making it safer for use in CI environments and on shared RHEL 7 build servers.

Step 9: Running the Final Image with Podman

# Run the image interactively
podman run -it --rm myapp:1.0.0 bash

# Run as a background service
podman run -d --name myapp-container -p 8080:8080 myapp:1.0.0

# Check running containers
podman ps

# View container logs
podman logs myapp-container

# Stop and remove
podman stop myapp-container
podman rm myapp-container

Conclusion

Podman and Buildah give RHEL 7 administrators a fully supported, daemonless container workflow that produces OCI-compliant images without the security surface area of the Docker daemon. Buildah’s scripting-first approach — using buildah from, buildah run, buildah copy, and buildah config — is especially powerful for building minimal scratch images and integrating image construction into shell scripts or CI pipelines. Combined with Podman’s Docker-compatible runtime, these tools provide everything you need to build, run, and distribute containers on RHEL 7 in a manner fully aligned with Red Hat’s supported stack. As a next step, consider generating systemd unit files from your Podman containers using podman generate systemd to manage container lifecycle with systemctl.