You upgraded your CI runner to Fedora 40 and the container build step fails with a permission denied error. Or you are working on a laptop and want to assemble an OCI image without running a background daemon that listens on a privileged port. You tried a traditional container engine and hit a wall. You need a tool that respects rootless workflows, plays nicely with SELinux, and lets you inspect every layer before committing it.
What is actually happening
Traditional container engines rely on a client-server model. You send a command to a daemon, the daemon talks to the kernel, and you wait for a response. Buildah drops the daemon entirely. It operates as a standalone binary that manipulates container images directly on the filesystem. When you run a Buildah command, it reads the storage configuration, applies the OCI image specification, and writes layers to disk using a configured storage driver.
Think of it like assembling furniture from a flat-pack box. Instead of hiring a contractor who works in a separate workshop, you open the box on your own floor. You place each board exactly where you want it, check the alignment, and only screw the pieces together when you are satisfied. Buildah gives you direct access to the container filesystem at every step. You can mount a working container, edit files, run commands, and commit changes without ever handing control to a background process.
This architecture matters for two reasons. First, it removes the single point of failure that a daemon represents. Second, it makes rootless builds straightforward because the tool never needs elevated privileges to manage its own state. Fedora ships with the necessary storage drivers and security contexts preconfigured, but you still need to align your user configuration with what Buildah expects. The OCI specification defines how layers stack, how metadata is stored, and how runtimes execute the final image. Buildah speaks that specification natively.
Install and configure the toolchain
Install the package through the standard repository. Fedora keeps Buildah updated alongside the rest of the container ecosystem. Run dnf upgrade --refresh weekly to pull in security patches for the storage drivers and OCI libraries.
sudo dnf install -y buildah
# Install the FUSE backend required for unprivileged overlay mounts
sudo dnf install -y fuse-overlayfs
# Verify the binary is on your PATH and check the version
buildah version
Fedora enables rootless containers by default, but the storage driver determines whether your builds succeed or fail with cryptic permission errors. The overlay driver requires fuse-overlayfs for unprivileged users. If you skip this package, Buildah falls back to vfs, which works but consumes significantly more disk space and slows down layer caching.
Check your current configuration to confirm the driver is set correctly.
buildah info | grep -A 2 "graphDriverName"
# If it shows vfs, edit the rootless config to force overlay
# The file lives in your home directory, not in /etc
nano ~/.config/containers/storage.conf
Edit the storage configuration file to point to the correct driver. The rootless configuration lives in ~/.config/containers/storage.conf. Do not edit the system-wide file in /etc/containers/ unless you are configuring a shared server. User-level changes take precedence and survive package updates. This mirrors the standard Linux convention where /etc/ holds user modifications and /usr/lib/ holds package defaults.
[storage]
# Force the fuse-overlayfs backend for rootless builds
driver = "overlay"
Save the file and run buildah info again. The output should confirm graphDriverName: overlay. If you see a warning about missing kernel support, your system lacks the required mount namespaces. Reboot into a newer kernel or switch to vfs as a temporary workaround.
Run buildah info before you start building. A misconfigured storage driver wastes hours debugging layer errors.
Build an image from a Dockerfile
Most workflows start with a Dockerfile. Buildah reads the same syntax and translates it into direct filesystem operations. Create a minimal Dockerfile in a working directory.
cat > Dockerfile << 'EOF'
FROM registry.fedoraproject.org/fedora:latest
RUN dnf install -y curl && dnf clean all
COPY . /app
WORKDIR /app
CMD ["curl", "--version"]
EOF
Execute the build command. The -t flag tags the image, and the trailing . tells Buildah to read the Dockerfile from the current directory.
buildah build -t my-fedora-app:latest -f Dockerfile .
# The --layers flag is default but explicit here for clarity
# Buildah caches each RUN step to speed up subsequent builds
# Failed steps leave the intermediate container on disk for inspection
Watch the output. Buildah prints each step as it executes. If a RUN command fails, the build stops immediately and leaves the intermediate container on disk for inspection. This is different from engines that clean up automatically on failure. You can examine the broken state, fix the Dockerfile, and resume. The caching mechanism only skips steps when the underlying Dockerfile instruction and the base layer hash remain identical.
Interactive layer construction
Sometimes a Dockerfile gets too complex or you need to debug a dependency chain. Buildah lets you create a working container, mount its filesystem, and run commands manually.
# Create a working container from the base image
buildah from --name debug-container registry.fedoraproject.org/fedora:latest
# Mount the container's root filesystem to a local directory
buildah mount debug-container
# The output gives you a path like /var/lib/containers/storage/overlay/.../merged
Copy files or run commands inside the mounted directory. You can use standard Linux tools like cp, sed, or tar directly on the mounted path. When you are done, unmount and commit the changes.
# Unmount the working directory
buildah unmount debug-container
# Commit the working container to a new image
buildah commit debug-container my-fedora-app:manual
# Remove the working container to free disk space
buildah rm debug-container
This workflow is invaluable when you are troubleshooting a broken package dependency or testing a custom configuration. You can inspect the filesystem, adjust permissions, and commit only when everything aligns. Working containers consume disk space until you explicitly remove them. Run buildah containers to list active sessions and buildah rm to clean them up.
Mount the container before you edit files. Direct filesystem manipulation bypasses layer caching and keeps your image lean.
Verify the build
Confirm the image exists and inspect its metadata. The images command lists everything in your local store. The inspect command reveals the command history, environment variables, and layer chain.
buildah images
# Filter by repository name to avoid clutter
buildah images --filter reference=my-fedora-app
# Inspect the final image to verify the CMD and labels
buildah inspect my-fedora-app:latest
Look for the Cmd field and the Config section. If the output matches your Dockerfile, the image is ready for deployment. Push it to a registry when you are satisfied.
buildah push my-fedora-app:latest docker://localhost:5000/my-fedora-app:latest
# The docker:// prefix tells Buildah to use the Docker registry protocol
# Add --tls-verify=false only for testing against insecure local registries
# Push operations respect the same storage driver limits as builds
Push to a registry after you verify the image. Local storage fills up fast when you leave working containers behind.
Common pitfalls and error patterns
Rootless builds fail most often due to storage driver mismatches or missing FUSE support. You will see this error when fuse-overlayfs is absent or the kernel lacks user_namespace support:
Error: creating container storage: creating overlay mount to ...: operation not permitted
Install fuse-overlayfs and verify your kernel version. Fedora 38 and newer enable the required namespaces by default. If you are on an older release, upgrade the kernel or switch to the vfs driver temporarily.
SELinux denials appear when you mount a container filesystem to a directory with the wrong context. Buildah handles most contexts automatically, but custom bind mounts sometimes trigger an AVC denial. Check the audit log for the exact path and operation. Use journalctl -xe to read the full context around the failure. The x flag adds explanatory text and the e flag jumps to the end.
sudo ausearch -m avc -ts recent | grep buildah
# Restore the default context if you manually created a directory
sudo restorecon -Rv /path/to/build/context
Do not disable SELinux to fix container errors. The denial usually points to a missing label or a misconfigured bind mount. Fix the context and retry. SELinux denials show up in journalctl -t setroubleshoot with a one-line summary. Read those before touching security policies.
Read the audit log before you change security policies. SELinux denials are precise, not random.
When to use Buildah versus alternatives
Use Buildah when you need a daemonless tool to construct OCI images in CI pipelines or rootless environments. Use Podman when you want to run, manage, and orchestrate containers with a Docker-compatible CLI. Use Docker when you are maintaining legacy workflows that depend on the Docker socket and official Docker Hub plugins. Stay on Buildah for image assembly and switch to Podman for runtime execution.