Containers are run in the cloud. That's because container technology allows websites and web apps to spawn fresh copies of themselves as demand increases. They're the reason hundreds of millions of people can use popular sites without those sites buckling under the pressure of global traffic. Containers are a Linux technology, meaning that they rely on code (specifically
cgroups and namespaces) unique to the Linux kernel, so when you run a container, you're running Linux. Using container images from sites like quay.io and dockerhub.io, most people build new containers specific to their application or use case. But that makes some people wonder: If my container comes from a developer building on top of another developer's container, where do those containers come from? Don't worry, it's not turtles all the way down. You can build a container from scratch, and there's a great open source tool called Buildah to help you do it.
Containers grew out of projects like Linux containers (LXC) and Docker, and it's the Open Container Initiative (OCI) that maintains the formal specification of what a container is. A properly assembled container that meets the OCI definition runs on any OCI-compliant container engine, such as Podman, Docker, CRI-O, and so on.
On Fedora and CentOS, you may have Buildah already installed. If not, you can install it with your package manager:
$ sudo dnf install buildah
On Debian and Debian-based systems:
$ sudo apt install buildah
Because Buildah creates containers, configuring your environment for it is the same as configuration for Podman. Whether or not you're using Podman, configure your system for "rootless" podman before continuing.
Building a container out of nothing
To build a brand-new container, using nobody's prior work as your foundation, you use the special name
scratch to tell Buildah that you want to create an empty container. The
scratch designation is not an image name. It's your exemption from using an existing image to base your work on.
$ buildah from scratch
This new container, named
working-container by default, features a small amount of metadata and literally nothing else, and it's secretly running in the background now. You can see it with the
$ buildah containers
CONTAINER ID BUILDER ID IMAGE NAME CONTAINER NAME
dafc77921c0c * scratch working-container
To run the container, you must first use the
unshare subcommand (unless you're running Buildah as root):
$ buildah unshare
Confirm that your working container has no functionality (failure expected response in this instance):
$ buildah run working-container sh
ERRO container_linux.go:349: starting container process caused "exec: \"sh\": executable file not found in $PATH"
Adding to your container
To add commands to your container, you must mount it first. Container images are stored in your
~/.local directory by default:
$ buildah mount working-container
With the container mounted to your
~/.local directory (or
/var/lib/containers/ in the case of running as root), you can add packages using your package manager. The
--releasever must match the distribution you're running as you build the container.
[Fedora]$ sudo dnf install --installroot \
--releasever 33 \
bash coreutils \
--setopt install_weak_deps=false -y
The exact method of adding packages depends on your distribution and the package manager it uses. For example, on my Slackware desktop, I use
[Slack]$ installpkg --root ~/.local/share/containers/storage/overlay/b76940e6fe4efad7a0adca3b5399ee12055ddd733bbe273120dcae36a2e6c12f/merged \
Now you can run the container and try something simple, like launching a shell:
$ buildah run working-container bash
# bash --version
GNU bash, version 5.0.17(1)-release (x86_64-redhat-linux-gnu)
Copyright (C) 2019 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
This is free software; you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.
Configuring your container
buildah config subcommand gives you access to common attributes such as the default command you want your container to run when it's launched, set environment variables, set the default shell, define the author, architecture, and hostname, and much more. For instance, imagine that you have added a package containing a shell script called
motd.sh, and you want it to run when the container is launched:
$ buildah config --author "Seth Kenlon" \
--os "Slackware" --shell /bin/bash \
--cmd /usr/bin/motd.sh working-container
Distributing your container
When you're finished constructing your container, you can preserve it as an image using the
$ buildah commit working-container my_image
Build it with Buildah
Containers sometimes seem magical, but they're not magic. They're built from the ground up, and they're flexible enough that once an image exists, others can use it to build new containers and container images that fill a different niche. It's not necessary to start from scratch, but if you're curious how images start, or you want to try to create an image specific to your requirements, Buildah is the tool to use.