Skip to main content

Build OCI images with Buildah on Sourcehut

I like the concept of containerization. Isolating [1] applications and deploying them with only a few commands on a large fleet of servers surely has its benefits. One major and importen part of every container setup is automation. If you are running arround and are building your container by hand you are doing it wrong. So let's take a look how you can automate your container building on the excellent sourcehut infrastructure.

For this tutorial we will use the following workflow:

Creating the Dockerfile --> Commiting to git.sr.ht -->
Building and tagging the image via builds.sr.ht --> Pushing the image to a registry

As an example project i will be using my xdarkhttpd-docker project. Darkhttpd is a basic webserver originally written by Emil Mikulic. I am using a fork by Ryan M. Jacobs which supports basic auth and am extending it by adding some more mimetypes (for now). [2]

Step 1: Creating the Dockerfile

Since the main effort lies in creating the Dockerfile from which our image will be built we will take a closer look at that. At first i used Alpine as base image and compiled darkhttpd in it. This resulted in an image which was around 9 to 10 Mb in size. After trying to remove some bloat (something you should always strive for) i was able to reduce the image size to around 7 Mb by using a multi-stage build.

This method builds the binary in one container including all build dependencies and then copies the binary over into a second container. 7Mb is okayish but we can go even smaller. If we are able to build a static binary we can use an empty image (called scratch) to house and run the binary and a work directory. Which is what i did. The whole xdarkhttpd image is now < 700kB in size. (I have to add that darkhttpd in itself is a very rudimentary server. No cgi, no dynamic content, etc.)

Without further ado here is the complete Dockerfile:

from alpine:latest as builder

RUN apk --no-cache add make musl-dev git gcc
run git clone https://github.com/ryanmjacobs/darkhttpd
workdir darkhttpd
copy filetype.patch .
run patch darkhttpd.c < filetype.patch
run LDFLAGS=-static make bin

from scratch
LABEL maintainer="code@project47.xyz"
expose 8080
#workdir /usr/local/bin
copy --from=builder /darkhttpd/darkhttpd .
workdir /data
entrypoint ["../darkhttpd", ".", "--port", "8080"]

Normally i would commit this Dockerfile and the filetype.patch to a github repo and would connect my Docker Hub Account to it. Then i would add a build job on Docker Hub and it would pull everything from the repo build the image and then commit it to the registry. But we are here to talk about how we would achieve the same on sourcehut. Cue the next step...

2. Create a .build.yml to tell builds.sr.ht what to do

Sourcehut has no interface to configure the build system. Instead it relies on a YAML-file which you can add to your repository. I will publish my .build.yml and then will talk about some aspects which are not intuitive (not because of sourcehut but the whole process).

image: fedora/30
packages:
  - buildah
sources:
  - https://git.sr.ht/~containsliquid/xdarkhttpd-docker
secrets:
  - b525cc29-e3ad-488e-8b07-f267e3db3532
tasks:
  - build_image: |
      cd xdarkhttpd-docker
      buildah bud -f Dockerfile -t containsliquid/xdarkhttpd:latest
  - push_image: |
      buildah login docker.io
      buildah push --format docker containsliquid/xdarkhttpd:latest docker://docker.io/containsliquid/xdarkhttpd:latest

I won't go into too much details since there is enough documentation and references on sourcehut for this matter. One of the biggest changes in the standard workflow is that i am not using Docker to build the image. But why? Docker, even though they are one of the two big players in the field of containerization, has a few drawbacks. And especially two of them are pretty annoying in this case:

  • The need for root to buld and run containers, which can cause problems in build environments [3]
  • You need external repositories to have an up-to-date version of docker

Since it is my goal to make the process as simple as possible i opted in to use Buildah instead. Buildah is a tool developed by Redhat and is following the OCI specification for container images. Buildah does not require root to run and is also part of the standard Fedora/Centos repos. Buildah works very similar to Docker. So we do not need to change things in our Dockerfile. With the command in line 11 we are building and tagging the image. After the build_image task we start the push_image task. We log into Docker Hub and push the image to the repo there. Easy right?

3. Hurdles in the process

3.1. Docker Hub Login

This one is a bit tricky. The login process of Buildah requires you to pass your credentials either via --username & --password or via authfile. Usually this file is located in ~/.docker/config.json. Luckily enough you can add secrets on sourcehut. At this time sourcehut supports ssh-, pgp-keys and files as secrets. So log into the registry of your choice on your local machine (Docker or Podman/Buildah will work). Locate the corresponding config.json and add it to the sourcehut secret store. Remember to add the correct path for the file so Buildah can find the file. You are now able to log into the registry on builds.sr.ht.

3.2. The weird OCI compatibility of Docker Hub

This is something which completely is the fault of Docker Inc.

  1. Their registry on hub.docker.com is a V1 registry which means that it does not work with software that is following the OCI specification. [4] This problem can be circumvented by using docker.io as base domain.
  2. Pushing an OCI compliant image to docker.io works in Buildah but the image won't show in the web ui. This is a weird one. I don't know if currently their registry or the frontend is broken. But you can add a --format docker flag to your push command and everything is working fine.

4. Conclusion

Sourcehut is an excellent environment to build and deploy container images. There are a few things to consider but all in all the workflow is not that different to github. The major benefit of using sourcehut over github is independance from the big silos. Furthermore sourcehuts buildsystem offers more flexibility than Dockers own system. One thing i will look into in the future is automated inspection and analysis of images. So stay tuned...

Thank you for reading. This is my first blog post after a long time. And my first blog post in english. If you want to comment on it feel free to do so via Mastodon/Activity Pub. You can find my account linked at the top of the page.

[1] Isolation in this context is mainly referring to bundeling dependencies not security.
[2] Thus calling it e(x)tended darkhttpd or shor xdarkhttpd. I am awul at naming thing.
[3] I never tested it, but i think docker would work fine on sourcehut. But i wanted to show an approach that could potentially work everywhere and does not rely on the right privileges to talk to daemon running as root.
[4] V1 was Dockers own format for container images. Since the format has been specified by the Open Container Initiative version 2 of their registry is compatible with OCI compliant images.