Containers & Orchestration·6 min read·

Building Images with Buildah Without a Daemon

Assemble OCI container images with Buildah's rootless, daemonless build steps and combine them with systemd or CI for reproducible pipelines.

NB

Netbay Engineering

Netbay Engineering

On this page

Most people build images with a Dockerfile and a build daemon, but there is an older, more flexible way to assemble an OCI image: run the build steps yourself with Buildah and commit the result. Because Buildah works without a daemon and without needing root for common paths, you can script image construction the way you script any other deployment step — inside a systemd unit, a cron job, or a CI runner on a VPS.

The real value is control. You see every layer that gets added, you decide how to mount working files, and you can produce a distroless or scratch image with a single container command, no Dockerfile required.

The Buildah Model

Buildah separates three ideas that Dockerfile builds blur together: creating an empty container, running build steps, and committing those steps to an image. Work happens in a working container you control directly:

bash
# create an empty working container from a base image
buildah from --name webbuild nginx:alpine
# copy files in, no Dockerfile
buildah copy webbuild ./dist /usr/share/nginx/html
# run a build step inside the container
buildah run webbuild chmod -R 755 /usr/share/nginx/html
# commit to an image tag
buildah commit webbuild mysite:1.0
buildah rm webbuild

Each step adds a layer; the working container is just a scratch space you commit.

base image copy files run step commit image steps become layers; no daemon involved

Each buildah copy or buildah run before a commit becomes its own layer, so you can read the image history afterward and see exactly what happened.

From Containerfile Too

If you prefer a declarative container file, Buildah reads it happily. buildah bud means "build using the Dockerfile" and uses the same layer caching:

bash
buildah bud -t mysite:1.0 .
buildah pull docker.io/library/alpine:latest
buildah images
buildah push mysite:1.0 docker://ghcr.io/you/mysite:1.0

Because there is no daemon, builds run entirely in your user's context and network, which makes them predictable to debug and easy to run from a systemd timer for scheduled rebuilds.

Squashing and Distroless Outputs

One buildah strength is producing tiny final images. buildah bud --squash flattens the layers of a Containerfile build into one. And since working containers are just filesystem snapshots, you can also layer files onto a scratch base and get a naked single-binary image:

bash
buildah from scratch
buildah copy --add-history working /app /app
buildah commit --rm working myapp:scratch
podman run --rm myapp:scratch /app/server

This pairs well with statically compiled Go or Rust binaries, which need no libc from the image.

Putting Buildah in a Pipeline

Buildah is a natural fit for the rootless, systemd-first workflow. A systemd timer can rebuild an image from a git checkout on a schedule, then restart the Quadlet that runs it — an unglamorous but very reliable CD loop.

bash
#!/bin/sh
set -e
cd /srv/app
git pull --ff-only
buildah bud -t myapp:latest .
ctr=$(buildah from myapp:latest)
systemctl --user restart web

Takeaway

Buildah gives you daemonless, rootless, scripted image creation with every layer visible and full control over the final artifact, including scratch and distroless outputs. It slots into systemd and CI cleanly on a plain Linux VPS. Run this pipeline on a Netbay instance — provision it at netbayhosts.in and rebuild and deploy your images with a cron or timer, no cluster needed.

Keep reading

Follow along on a real VPS

Deploy Linux in under 60 seconds

These guides are written against Ubuntu, Debian, and RHEL-family images — the same ones on NetBay.

Deploy an instance