The container build that refuses to run
You have a working application on your Fedora desktop. You want to package it so a colleague can run it without wrestling with dependency conflicts. You write a Dockerfile, run the build command, and the terminal throws a permission error or asks for a root password. You are trying to build a container image, but the traditional toolchain expects a background daemon you do not want to install. This is where Buildah steps in.
Why Buildah exists
Container images are just layered filesystems paired with a manifest file. Traditional tools require a background daemon to assemble those layers. The daemon runs as root, manages a Unix socket, and holds the entire build context in memory. Buildah takes a different approach. It builds images directly from the command line without a daemon. It uses standard Linux namespaces and cgroups to isolate the build environment. Think of it like assembling furniture with a single wrench instead of renting a whole workshop. You get the exact same result, but you keep control of every step.
Fedora ships with rootless containers enabled by default. Your user account already has a subuid and subgid range configured during installation. Buildah respects this configuration out of the box. You do not need sudo for most build operations. The tool writes image layers to your home directory under ~/.local/share/containers/ and reads configuration from ~/.config/containers/. This separation keeps your system packages clean and your container work portable. Config files in /etc/containers/ apply system-wide. Files in ~/.config/containers/ override them for your user. Edit the home directory version. Never touch the system version unless you are managing a shared build server.
Building your first OCI image
Install the package first. Fedora provides Buildah in the official repositories.
sudo dnf install buildah
# WHY: dnf resolves dependencies and installs the CLI tool.
# WHY: sudo is only needed here because package installation modifies system directories.
# WHY: Rootless builds after this point will not require elevated privileges.
Create a working directory for your project. Buildah expects a Containerfile or Dockerfile in the build context. The terms are interchangeable, but the upstream community prefers Containerfile to avoid vendor lock-in.
mkdir ~/my-app-build && cd ~/my-app-build
# WHY: Isolates the build context from your home directory clutter.
# WHY: buildah bud reads files relative to the current working directory.
# WHY: Keeping the context small speeds up the layer transfer.
Write a minimal Containerfile. This example uses the official Fedora base image and copies a simple script into the container.
FROM fedora:41
# WHY: Starts with a known-good base image from the Fedora registry.
# WHY: The tag pins the release version to prevent unexpected upstream changes.
# WHY: Base images are cached locally after the first pull.
RUN dnf install -y python3 && dnf clean all
# WHY: Installs runtime dependencies inside the build container.
# WHY: dnf clean all removes the package cache to keep the final layer small.
# WHY: Layer caching means this step only re-runs if the command changes.
COPY app.py /app/
# WHY: Copies your application code into the container filesystem.
# WHY: COPY is preferred over ADD for static files to avoid hidden tar extraction.
# WHY: Placing files in /app/ keeps the root directory uncluttered.
CMD ["python3", "/app/app.py"]
# WHY: Defines the default executable when the container starts.
# WHY: JSON array syntax bypasses the shell and prevents signal handling issues.
# WHY: This instruction sets the entrypoint for runtime, not build time.
Run the build command. Buildah uses the bud subcommand, which stands for build using dockerfile. The flag names match Podman and Docker for familiarity, but the execution path is entirely local.
buildah bud -t my-app:latest .
# WHY: bud reads the Containerfile in the current directory.
# WHY: -t tags the resulting image so you can reference it later.
# WHY: The dot specifies the build context directory for COPY instructions.
Watch the terminal output. Buildah will pull the base image, create a working container, execute each instruction in a new layer, and commit the result to the local image store. The process runs entirely in your user namespace. No daemon socket is contacted. No background process survives after the command finishes. The OCI image format is the industry standard. Docker images are a subset of OCI. Buildah produces OCI-compliant manifests by default. You do not need to specify a format flag unless you are pushing to a legacy registry that rejects OCI schemas.
Verify the image and push it
Check your local registry to confirm the image exists.
buildah images
# WHY: Lists all images stored in your local container storage.
# WHY: Shows the repository name, tag, image ID, and creation timestamp.
# WHY: Verifies that the build transaction committed successfully.
Test the image before pushing it to a remote registry. Podman is the standard runtime on Fedora and reads the same local storage as Buildah.
podman run --rm my-app:latest
# WHY: Runs the container and removes it automatically after exit.
# WHY: Validates that the CMD instruction executes without runtime errors.
# WHY: Confirms that all dependencies resolved correctly inside the image.
Push the image to a registry when you are ready to share it. Buildah handles authentication by reading your existing container credentials.
buildah push my-app:latest docker://quay.io/username/my-app:latest
# WHY: docker:// prefix tells Buildah to use the Docker registry protocol.
# WHY: Buildah reads credentials from ~/.docker/config.json or the system store.
# WHY: The push streams layers directly without creating temporary disk files.
If the push fails with an authentication error, log in first. The credential helper stores the token securely in your keyring.
buildah login quay.io
# WHY: Prompts for username and password and stores the token.
# WHY: The token is cached for subsequent push and pull operations.
# WHY: You only need to run this once per registry per machine.
Run buildah push again after authentication succeeds. The registry will accept the layers and return a digest hash. Keep your registry credentials out of version control. Use environment variables or a secret manager for CI pipelines.
Common pitfalls and error messages
Buildah runs in a rootless environment by default. This changes how storage drivers behave. Fedora uses overlay for root containers and fuse-overlayfs for rootless containers. If you see a storage error, check your driver configuration.
Error: initializing source docker://fedora:41: Error initializing source docker://fedora:41: Error pinging docker registry registry.fedoraproject.org: Get "https://registry.fedoraproject.org/v2/": dial tcp: lookup registry.fedoraproject.org: no such host
This error means your network cannot reach the registry. Check your DNS configuration or proxy settings. Container tools do not bypass system networking. If you are behind a corporate proxy, set HTTP_PROXY and HTTPS_PROXY in your shell environment before running the build.
Error: applying layer: applying tar: exit status 1: overlay: cannot create overlay mount: invalid argument
This error appears when the fuse-overlayfs package is missing or the kernel module is not loaded. Install the package and restart your terminal session.
sudo dnf install fuse-overlayfs
# WHY: Provides the userspace overlay filesystem driver for rootless containers.
# WHY: Required when the kernel overlay driver lacks rootless support.
# WHY: Replaces the slow vfs driver with a performant FUSE implementation.
SELinux denials occasionally block container operations. The default Fedora policy allows rootless containers, but custom firewall rules or restrictive apparmor profiles can interfere. Check the audit log before disabling security modules.
journalctl -xeu containers-storage.service
# WHY: Shows recent logs from the container storage backend.
# WHY: The -x flag adds explanatory text to standard error codes.
# WHY: The -e flag jumps to the end of the journal for quick review.
If you are building images that require hardware acceleration or privileged operations, you will hit namespace restrictions. Rootless containers cannot mount certain kernel interfaces or change network namespaces without polkit approval. Run the build with sudo only when you absolutely need root privileges. Prefer rootless operation for standard application packaging. Trust the package manager. Manual file edits drift, snapshots stay.
Buildah versus Podman versus Docker
Use Buildah when you need to construct images without a daemon or want to inspect intermediate layers during development. Use Podman when you need to run, manage, and orchestrate containers on the same machine. Use Docker when you are working in an environment that mandates the Docker daemon and socket API. Stay on the rootless Buildah workflow if you only need to build images and push them to a registry.
Buildah excels at CI/CD pipelines where you want to build an image, push it, and tear down the build environment without leaving background processes. Podman excels at local development where you need to run containers, map ports, and manage volumes. Docker remains the standard for legacy tooling and certain cloud platforms that expect the /var/run/docker.sock interface. Fedora does not ship Docker by default. The ecosystem has moved to OCI-compliant tools that share storage and configuration. Run journalctl -xe first. Read the actual error before guessing.
Where to go next
- How to Generate systemd Service Files from Podman Containers
- How to Build a Container Image from a Containerfile (Dockerfile) on Fedora
- Set up Docker development
Keep your Containerfile lean. Every unnecessary layer increases build time and attack surface. Commit the image early. Push it often.