DJ Adams

How I run executables in containers

Here's a quick post explaining how I might typically containerise an executable. This is in the context of my aim to not "pollute" my base OS (at the bare metal level) with any installs, as far as I can, as well as to remain platform independent and abstract.

Background

Last Friday saw the second SAP Inside Track Madrid event, which was excellent. My good friend and colleague Antonio, one of the core event organisers, kicked things off, and then I started with my talk, "Level up your CAP skills by learning how to use the cds REPL".

I'd written my talk also in blog post form, for folks who might also be interested but couldn't attend the event in person, but also for the folks in the room, especially as I wanted to make clear that everything I was about to demonstrate (I conducted my talk as a live REPL session in the terminal) was easily repeatable, and they could follow the examples at home afterwards by reading the post.

Generating and displaying a QR code in the terminal

So I decided to be ready to share a link to the blog post right at the start of the talk, and thought I'd have a bit of fun trying to generate a QR code in the terminal, large enough for the attendees to scan on their phones from where they were sat in the audience. For this, I found a great tool called QRCode Terminal, in the form of a binary called qrterminal.

Here's a very grainy screen grab from a small picture-in-picture window of me presenting:

QR code on screen

Abstracting all the things

I am trying to avoid installing anything on this macOS laptop other than the basics, i.e. Docker. In previous times I would have typically had Homebrew installed and then used that to install various software packages, but I'm actively avoiding that, choosing instead to run everything in containers, either locally (i.e. on the container engine running on my laptop) or remotely (on my "homeops" server or on my Synology NAS).

I didn't have QRCode Terminal installed in my PDE (Personal Development Environment) which is also a container (based on Debian and running tmux, neovim, bash and so on), so I could have just apt installed it and executed it there, but I wanted to take the opportunity to improve my Dockerfile fu (one kata at a time).

The Dockerfile

So I created a simple Dockerfile, trying to use some best practices with respect to multi-stage builds and minimising image size. Here it is:

FROM alpine:3.21 AS base

ARG ARCH=arm64
ARG QRTERMINALVER=3.2.1

RUN wget \
  -O - \
  -q \
  https://github.com/mdp/qrterminal/releases/download/v${QRTERMINALVER}/qrterminal_Linux_${ARCH}.tar.gz \
  | tar xzf - -C / qrterminal

FROM scratch
COPY --from=base /qrterminal /
ENTRYPOINT ["/qrterminal"]

Here are some notes:

On this last point, I'd like to dig in more to see how I might improve this, especially with respect to those circumstances (like here) where I'm downloading a particular tool's binary release which has been built for a specific architecture which is also in the tarball URL name.

Here's the (cached) build output:

$ docker build --build-arg QRTERMINALVER=3.2.1 -t qmacro/qrt .
[+] Building 1.9s (7/7) FINISHED                                     docker:default
 => [internal] load build definition from Dockerfile                           0.0s
 => => transferring dockerfile: 448B                                           0.0s
 => [internal] load metadata for docker.io/library/alpine:3.21                 1.9s
 => [internal] load .dockerignore                                              0.0s
 => => transferring context: 2B                                                0.0s
 => [base 1/2] FROM docker.io/library/alpine:3.21@sha256:a8560b36e8b8210634f7  0.0s
 => CACHED [base 2/2] RUN wget   -O -   -q   https://github.com/mdp/qrtermina  0.0s
 => CACHED [stage-1 1/1] COPY --from=base /qrterminal /                        0.0s
 => exporting to image                                                         0.0s
 => => exporting layers                                                        0.0s
 => => writing image sha256:7731e4687c0a4272f2a06002c2bc933fdb5fe3e7c6f0b6f53  0.0s
 => => naming to docker.io/qmacro/qrt                                          0.0s

I was happy with the result, shown here via docker image ls:

IMAGE ID       REPOSITORY  TAG      SIZE
7731e4687c0a   qmacro/qrt  latest   1.64MB

And the best part was - it worked!

docker run --rm qmacro/qrt https://qmacro.org/blog/posts/2025/03/21/level-up-your-cap-skills-by-learning-how-to-use-the-cds-repl/

QR code screenshot