Buildah and podman build
`buildah` is already installed - it arrived with Podman. Build an image with no Containerfile at all: `buildah from`, `buildah run`, `buildah commit`, and shell loops instead of build instructions.
Building Images Guide 17 of 47 Intermediate
- OSUbuntu 26.04 LTS (resolute)
- Podman5.7.0
- Runtimecrun 1.21
- Networknetavark 1.16.1
- Buildah1.42.1
- TimeAbout 13 min
- Reviewed22 August 2026
Written against the versions above. Podman follows the distribution here rather than a vendor repository, so the version you get is the one Ubuntu shipped. The commands are stable across 5.x.
| Server Name | IP Address | OS | Roles | CPU | RAM | HDD |
|---|---|---|---|---|---|---|
| PODMAN01 | 192.168.0.24 | Ubuntu 26.04 LTS | Primary Container Host | 2 Core | 4 GB | 50 GB |
Before you start
- guide 30 - this guide does the same job without a Containerfile, and the comparison is the point.
-
It is already on the machine
command -v buildahanswers/usr/bin/buildah. You never installed it - it came in as a dependency ofpodman, which is listed among the 31 packages in guide 1.That is because
podman buildis a front end to it. The two share the image store, the storage driver, the registry configuration and the rootless machinery.buildah containerslists working containers - a separate concept frompodman ps, and empty right now.A working container is a container that exists to be modified and turned into an image, rather than to run a workload. That distinction is the whole reason buildah exists as its own command.
bash Example session command -v buildah podman/usr/bin/buildah/usr/bin/podmanbuildah --versionbuildah version 1.42.1 (image-spec 1.1.1, runtime-spec 1.2.1)buildah containersCONTAINER ID BUILDER IMAGE ID IMAGE NAME CONTAINER NAMEExpected resultBoth binaries in
/usr/bin, buildah 1.42.1, and no working containers.Success conditionYou know buildah is installed and what it means by a container.
-
Build an image with no Containerfile
Four commands replace a whole build file:
buildah from alpine:3.22- create a working container. It prints the name it chose,alpine-working-container.buildah run- run a command in it. This is a-- apk add --no-cache jq RUNinstruction, issued from your shell.buildah config --cmd '...'- set metadata. This isCMD.buildah commit- freeze it into an image.manual:1
Then
podman run --rm manual:1runs the result. Podman needed no introduction to it, because they share one store.Notice what this buys you: the build is a shell script rather than a declarative file. Every instruction is a command you can put in a loop, a conditional or a function, with variables from your environment and no build-arg plumbing. A Containerfile cannot do that; it has no control flow.
bash Example session buildah from docker.io/library/alpine:3.22alpine-working-containerbuildah containersCONTAINER ID BUILDER IMAGE ID IMAGE NAME CONTAINER NAMEbuildah run alpine-working-container -- apk add --no-cache jqfetch https://dl-cdn.alpinelinux.org/alpine/v3.22/main/x86_64/APKINDEX.tar.gzfetch https://dl-cdn.alpinelinux.org/alpine/v3.22/community/x86_64/APKINDEX.tar.gz(1/2) Installing oniguruma (6.9.10-r0)(2/2) Installing jq (1.8.1-r0)Executing busybox-1.37.0-r20.triggerOK: 8 MiB in 18 packagesbuildah config --cmd '/usr/bin/jq --version' alpine-working-containerbuildah commit alpine-working-container manual:1Getting image source signaturesCopying blob sha256:6f09edfb3f6d7173733adc8eec8ea00626550dc6fc2dcf07d40e13f5c1e907c4Copying blob sha256:270115d87057f8f30cb5da89e6fb752978fc0f786737c96b5824449e29fbc35bCopying config sha256:7128fd2a22d9034c79b4a3fe875331115f8e78e478710160ee3c392c0b551d19Writing manifest to image destination7128fd2a22d9034c79b4a3fe875331115f8e78e478710160ee3c392c0b551d19podman run --rm manual:1jq-1.8.1buildah rm alpine-working-containere0eefd8bad071457ab47c67f44313e1fc7015fa4d41461f33887bd12a7d44e82Expected resultA working container, a package installed into it, a commit, and the committed image running under Podman.
Success conditionYou built and ran an image without writing a build file.
-
When to reach for which
Use a Containerfile for almost everything. It is declarative, it caches, everyone can read it, and it works in every CI system without explanation. guide 32 is a property you get for free and give up here - buildah has no layer cache across runs, because there is no instruction list to compare.
Reach for buildah in the cases a Containerfile genuinely cannot express:
- The build needs real logic - iterate over a list of plugins, branch on the target architecture, read a value from a service.
- You want an image built from nothing:
buildah from scratchplus a single copied binary, with no base image and no package manager anywhere in the result. - A secret must never touch a layer, and you would rather mount it around the build than trust
--mount=type=secret. - You are generating images programmatically, where writing a Containerfile to a temp file first is the awkward part.
The useful mental model:
podman buildreads a file and calls buildah for each line. Anything it does, you can do by hand - and the reason not to is that the file is easier to read than your script.bash Example session buildah containersCONTAINER ID BUILDER IMAGE ID IMAGE NAME CONTAINER NAMEExpected resultAn empty working-container list again, after the
buildah rm.Success conditionYou can name a build this approach suits better than a Containerfile.
Troubleshooting
buildah: command not found.Why: Some distributions package it separately from Podman.
Fix:
sudo apt-get install -y buildah. On Ubuntu 26.04 it arrives withpodmanalready.podman imagesdoes not show an imagebuildah commitjust created.Why: Almost always a rootless/rootful mismatch - one was run under
sudo.Fix:
buildah info --format '{{.store.GraphRoot}}'and the podman equivalent must match. See guide 14.Working containers pile up.
Why:
buildah fromcreates one every time and nothing removes them automatically.Fix:
buildah containersto list,buildah rm --allto clear. They hold disk in the same store as your images.