CertGrid CertGrid
Concepts·Podman

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

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.

Every command on this page ran on podman01.
Server NameIP AddressOSRolesCPURAMHDD
PODMAN01192.168.0.24Ubuntu 26.04 LTSPrimary Container Host2 Core4 GB50 GB

Before you start

  1. It is already on the machine

    command -v buildah answers /usr/bin/buildah. You never installed it - it came in as a dependency of podman, which is listed among the 31 packages in guide 1.

    That is because podman build is a front end to it. The two share the image store, the storage driver, the registry configuration and the rootless machinery. buildah containers lists working containers - a separate concept from podman 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 NAME

    Expected 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.

  2. 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 -- apk add --no-cache jq - run a command in it. This is a RUN instruction, issued from your shell.
    • buildah config --cmd '...' - set metadata. This is CMD.
    • buildah commit manual:1 - freeze it into an image.

    Then podman run --rm manual:1 runs 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-containere0eefd8bad071457ab47c67f44313e1fc7015fa4d41461f33887bd12a7d44e82

    Expected 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.

  3. 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 scratch plus 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 build reads 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 NAME

    Expected resultAn empty working-container list again, after the buildah rm.

    Success conditionYou can name a build this approach suits better than a Containerfile.

Troubleshooting

Official sources