Buildah 919,369 (github.com/podman-container-tools/buildah (https://github.com/podman-container-tools/buildah 9,040 ), Apache-2.0) is the build engine inside podman 47,580 build, and on its own it builds images without any Dockerfile. Each Dockerfile instruction becomes a command you can run from a shell script, with ordinary shell logic between them: buildah from creates a working container, copy, run and config change it, and commit writes the image. Build BookNest's static front end that way, as the host user, with no daemon and no root:
c=$(buildah from -q docker.io/library/nginx:1.30-alpine)
buildah copy -q $c public/ /usr/share/nginx/html/
buildah run $c -- nginx -v
buildah config --label org.opencontainers.image.title=booknest-static --port 80 $c
buildah commit -q $c booknest-static:1.3 >/dev/null && buildah rm $c >/dev/null
podman run -d --name l3-static -p 127.0.0.1:33381:80 booknest-static:1.3 >/dev/null; sleep 1
curl -s localhost:33381 | grep -o '<title>.*</title>'; podman rm -f -t 0 l3-static >/dev/null
buildah unshare sh -c 'c=$(buildah from scratch); m=$(buildah mount $c)
echo "scratch root: $(ls -A $m | wc -l) entries"
buildah umount $c; buildah rm $c' | head -1nginx version: nginx/1.30.5 <title>BookNest</title> scratch root: 0 entries
Because each step is a separate command, one layer is committed only when you call commit, however many run and copy steps came before, which gives small images without chaining && in one RUN. The last command shows Buildah's most distinctive trick: buildah from scratch plus buildah mount exposes an empty root filesystem as a host directory, so a script can populate it with the host's own tools (for example dnf --installroot or a static binary copy) and ship an image that contains no package manager and no shell at all. Rootless, the mount only works inside buildah unshare, the user namespace where your UID is root. For CI, Buildah needs no privileged daemon socket, which makes it common in Kubernetes-hosted pipelines.