Buildah: A Complete Overview

A cartoon of a dog wearing a yellow hat and orange harness flying through a cloudy sky with parachutes attached to crates. Below, a man with gray hair and an orange scarf floats upside down. The scene has a whimsical, adventurous feel.
Last updated: | Published:

So far we’ve learned what container images really are - a packaging concept and a standard for container bundles. Knowing how they work at the lowest level doesn’t make it easier for us to use them. What does make it easier for us to use them is Buildah - a tool dedicated to building container images.

We’ve already tried another tool that is focused on working with the images, called Skopeo. Skopeo, as you might remember, is a tool to inspect and copy images.

Just like Skopeo is a tool focused on inspecting and copying images, Buildah is a small tool focused on building them. Imagine taking all the docker commands related to working with images, like docker pull, push and build, and moving them to a tool dedicated to working only with this commands - that’s Buildah.

Buildah is available in most of the default package repositories, so you can intall it by simply running yum install buildah -y.

Dockerfile vs Containerfile

Before we build our first image, let’s talk about Dockerfile. Already obvious from the name, Dockerfile is something very Docker-specific, right?

Historically, yes. The beauty of Dockerfile is that it’s more or less a standard format for defining the build process for your container images. The less attractive side of it, at least from the open-standards perspective, is that it contains the very specific technology name inside.

There is no official standard for Dockerfiles except the documentation from Docker, but there is a first suggestion from the community on how to move towards this direction. The suggestion is pretty simple: rename Dockerfile to Containerfile.

While there is no official description what Containerfile is, multiple tools already recognize this file automatically, including Buildah.

Renaming your Dockerfiles to Containerfiles at this moment is nothing but a nice gesture towards embracing the more standards oriented container world - at mkdev, we already do it since a while, you could do it - or you could also wait till something more official comes out of this effort.

Building container images with Buildah

Let’s build some container images with Buildah!

We start by creating a new Containerfile with the following contents:

FROM alpine


RUN apk update && apk add curl

To build the image, we need to run buildah build.

Notice how Buildah had no issues to pull the image from Docker Hub - that’s exactly because what we tend to call “docker images” are indeed just “container images”, that many different tools can work with.

To get the list of all the images we have, run buildah images.

You will see, that our new image has no tag or the name. We could run buildah build -t my-first-image:latest.

Then the image will have a name and a tag. We could then push this image with “buildah push” command, just like we would do with “docker push”. And, of course, we can pull the images with “buildah pull”. Buildah also supports protected registries, meaning you can run “buildah login” to get access to private registries.

There is another way to build images with buildah, which is much less convenient and fits only certain specific use cases - via simple shell scripts. This approach will allow us to understand what Buildah is doing a bit better.

First, let’s run buildah from alpine. This command will output a container name. If we run buildah ps we will see all containers that buildah is running.

It could be a bit unexpected for a container image tool to run containers, but in fact, you build a container image, you often need a container - this is the easiest way to ensure that the new container image does not depend on anything from the system where the image was built.

Next we need to run some commands in the container:

buildah run alpine-working-container -- apk update
buildah run alpine-working-container -- apk add curl

We can also configure things like environment variables by using the buildah config command:

buildah config -e ENVIRONMENT=test alpine-working-containe

We can also mount the complete file system of this container to a local directory:

buildah unshare
buildah mount alpine-working-container

This would allow us to modify the contents of the container filesystem by using any of the tools available on the host system - just keep in mind, that in this case you risk making your container image depend on the host system more than needed.

Finally, we can commit this container to be a container image:

buildah commit alpine-working-container localhost/my-first-image:latest

This command will take the container filesystem and package it as a new image. Afterwards, we still need to remove the working container:

buildah rm alpine-working-container

You could take all of the commands we’ve just run and put it in a script, and name this script “build.sh”. Then, running “build.sh” would give you a new image, essentially becoming an alternative to the Containerfile.

The fact is, when Buildah processes the Containerfile, it will internally do exactly the same steps that we just did by running all of those commands. It will also create a new container, run some commands inside of it, commit the resulting filesystem and remove the container.

Naturally, Containerfile is a much nicer way to describe the image build process. The second approach is quite low-level and might be needed only for more complex images, or for the cases where you integration buildah inside some other tools - which we will talk about in a later lesson.

There was one command that I didn’t talk too much about - buildah unshare. To talk about it, we have to finally discuss the rootless nature of Buildah

Rootless Containers

When we were using Buildah, we never had to run any command with root permissions - neither we had to add ourselves to a special user group that would have those extra permissions. The reason is that Buildah is a rootless tool - it does not need the root access to work with container images. There are a couple of technical decisions that allow Buildah to work without root for most of its operations.

Most importantly, Buildah is an independent command line tool. There is no daemon that needs to run on your machine for Buildah to work - Buildah binary is everything you need to work with container images, just like Skopeo, for example.

The absence of the privileged daemon means that any user on the system can use Buildah to work with images - and it also means that images are scoped to the system user.

Let’s create a new system user and then login as this user.

sudo useradd kirill
sudo su kirill

If we try to list images now, we will see nothing:

buildah images

But once we try to build the image under this new user, we will see it in the list:

buildah bud -t new-user-image:latest

If we switch back to the original user, we won’t see the new-user-image in the list. And if we run “sudo buildah images”, we will also see a completely different list of images. Same applies to listing containers with buildah ps and any other command - everything is scoped to the user that is running Buildah.

Buildah is one of the tools that enables us to use “rootless containers”, meaning that unprivileged users are able to run containers and work with container images. Not only that, to be able to be truly rootless, the container runtime must not require any root privileges at all.

If you remember, when we used runc, it was also rootless - container process could be started from a regular user, and the information about the running container was scoped to the user that started the container.

If we take Docker as an example, it normally requires you to run a Docker Daemon with root privileges - so even if you can use Docker as a regular user, by adding this user to a special user group, it still can’t be considered a truly rootless way to run containers. Recent Docker releases have a rootless mode, that make use of all the operating system features to avoid requiring privileged access.

Rootless containers are normally achieved by using Linux namespaces as much as possible, with cgroups v2 on top for resource management. For local usage, you might not care about rootless containers too much - your system user normally has full root access anyway. But for server deployments, rootless container has fewer chances to get access to the host system - even if the attacker escapes the container, it still can only access what the user that has started the container can access.

Buildah applications

Now that you know what Buildah can do and how it works, the obvious questions is - what is it good for? After all, as a developer, you want to have a full package - not just to work with container images, but also run the containers.

Buildah, given its rootless capabilities, is a great tool to be used in various image building automations. If you want to build and promote and new container image in your CI/CD pipeline, Buildah can do. You can also safely run it inside a container, without this container having any root privileges.

This also makes Buildah a good internal component for other systems that are building images. One example are OpenShift BuildConfigs - a declarative and Kubernetes-native way to build container images on the cluster, that uses Buildah under the hood.

As for the local development, Buildah is a questionable replacement for Docker, simply because it’s not a complete container manager. It’s time for us to look at the most powerful and feature-complete container tool of this course - Podman.