buildah cover image
Figure 1. Cover Image: Building OCI images with Buildah

In the world of containers, Buildah stands out as a powerful yet lightweight tool for building OCI (Open Container Initiative)-compliant container images. Unlike traditional tools like Docker, Buildah takes a daemonless approach, making it secure, flexible, and ideal for modern development workflows.

Key Features

  • Daemonless Architecture: Buildah doesn’t rely on a background service (like Docker’s daemon). Instead, it directly manipulates images, ensuring a smaller footprint and lower resource usage.

  • Rootless and Secure: You can run Buildah without root privileges, making it safer, especially in multi-user environments or CI/CD pipelines.

  • Dockerfile-Free Flexibility: While Buildah supports Dockerfiles, it also enables image creation without one. Developers can use shell commands or scripts, offering full control over the image-building process.

  • OCI Compliance and Versatility: Buildah creates both OCI and Docker-formatted images, ensuring compatibility with tools like Podman, Docker, and Kubernetes.

  • Lightweight and Scriptable: Its minimal design makes Buildah perfect for automation and scripting, particularly in resource-constrained environments.

Installation

On Linux install it like this:

Debian
sudo apt-get -y install buildah
CentOS
sudo yum -y install buildah

For other Linux systems or for building from scratch, please take a look at the Buildah Website: Installation.

Using Buildah

Buildah allows us to derive custom images from a base image in no time, following a similar workflow:

Starting a container from a base image

starting a new container from an alpine base image
buildah from alpine
Example
buildah from alpine
Resolved "alpine" as an alias (/etc/containers/registries.conf.d/shortnames.conf)
Trying to pull docker.io/library/alpine:latest...
Getting image source signatures
Copying blob 1f3e46996e29 done
Copying config b0c9d60fc5 done
Writing manifest to image destination
Storing signatures
alpine-working-container

Modify a container

We want to install curl into our container…​

Modifying container, adding curl
buildah run alpine-working-container -- apk add --no-cache curl
Example
buildah run alpine-working-container -- apk add --no-cache curl
fetch https://dl-cdn.alpinelinux.org/alpine/v3.21/main/x86_64/APKINDEX.tar.gz
fetch https://dl-cdn.alpinelinux.org/alpine/v3.21/community/x86_64/APKINDEX.tar.gz
(1/9) Installing brotli-libs (1.1.0-r2)
(2/9) Installing c-ares (1.34.3-r0)
(3/9) Installing libunistring (1.2-r0)
(4/9) Installing libidn2 (2.3.7-r0)
(5/9) Installing nghttp2-libs (1.64.0-r0)
(6/9) Installing libpsl (0.21.5-r3)
(7/9) Installing zstd-libs (1.5.6-r2)
(8/9) Installing libcurl (8.11.1-r0)
(9/9) Installing curl (8.11.1-r0)
Executing busybox-1.37.0-r9.trigger
OK: 12 MiB in 24 packages

Add Files

Adding directory my-app and its contents
buildah copy alpine-working-container ./my-app /app
Example
buildah copy alpine-working-container ./my-app /app
e8cd61d7504140f22f673147bbdd78d3ac5f99d325bab53aafcfca029d689f84

Set metadata (modify environment variables and entrypoints)

Setting metadata
buildah config --env APP_ENV=production --entrypoint "/app/start.sh" alpine-working-container
Example
buildah config --env APP_ENV=production --entrypoint "/app/start.sh" alpine-working-container
WARN[0000] cmd "/bin/sh" exists but will be ignored because of entrypoint settings

Commit the Changes

Commiting changes
buildah commit alpine-working-container my-custom-image
Example
buildah commit alpine-working-container my-custom-image
Getting image source signatures
Copying blob a0904247e36a [--------------------------------------] 0.0b / 0.0b
Copying blob 0704e6598cc7 done
Copying config c353c06acc done
Writing manifest to image destination
Storing signatures
c353c06acc7fc4c66f1189a5f21bd94589e74b4f657634c3b518a5ad651b05c6

List containers

buildah ls
CONTAINER ID  BUILDER  IMAGE ID     IMAGE NAME                       CONTAINER NAME
5ceeca1b8928     *     b0c9d60fc5e3 docker.io/library/alpine:latest  alpine-working-container

Clean up

Cleaning up
buildah rm alpine-working-container
Example
buildah rm alpine-working-container
5ceeca1b8928d0a8d25456767429914b23fdc548bb1a76ac939dee275ef36be5

Working with Dockerfiles

Buildah can also work with Dockerfiles. Here’s how we can build an image using a Dockerfile:

Dockerfile
FROM alpine
RUN apk add --no-cache curl
COPY ./my-app /app
ENV APP_ENV=production
ENTRYPOINT ["/app/start.sh"]
Building the image
buildah bud -t my-custom-image .
Example
buildah bud -t my-custom-image .
STEP 1/5: FROM alpine
STEP 2/5: RUN apk add --no-cache curl
fetch https://dl-cdn.alpinelinux.org/alpine/v3.21/main/x86_64/APKINDEX.tar.gz
fetch https://dl-cdn.alpinelinux.org/alpine/v3.21/community/x86_64/APKINDEX.tar.gz
(1/9) Installing brotli-libs (1.1.0-r2)
(2/9) Installing c-ares (1.34.3-r0)
(3/9) Installing libunistring (1.2-r0)
(4/9) Installing libidn2 (2.3.7-r0)
(5/9) Installing nghttp2-libs (1.64.0-r0)
(6/9) Installing libpsl (0.21.5-r3)
(7/9) Installing zstd-libs (1.5.6-r2)
(8/9) Installing libcurl (8.11.1-r0)
(9/9) Installing curl (8.11.1-r0)
Executing busybox-1.37.0-r9.trigger
OK: 12 MiB in 24 packages
STEP 3/5: COPY ./my-app /app
STEP 4/5: ENV APP_ENV=production
STEP 5/5: ENTRYPOINT ["/app/start.sh"]
COMMIT my-custom-image
Getting image source signatures
Copying blob a0904247e36a skipped: already exists
Copying blob a2485dbc391e done
Copying config 24ffb5a676 done
Writing manifest to image destination
Storing signatures
--> 24ffb5a6765
Successfully tagged localhost/my-custom-image:latest
24ffb5a67653ba898d5f921eb461806b65494de08a45f5b80dea5498231af55e

Tutorial Sources

Please feel free to download the tutorial sources from my GitHub repository, fork it there or clone it using Git:

git clone https://github.com/hascode/buildah-tutorial.git

Resources