Crystal docker container to run Crystal bineries

I have a situation where I run Crystal in a docker container. What happens is that when the docker container is built up, it builds with the help of a Crystal image up binaries for the environment to run.
And then I selected a Crystal image to run the files. But the Crystal images are more or less only needed if you need to compile Crystal. And a normal Alpine image can’t run Crystal binaries due to the need for certain libraries.

I was able to build a docker image with those libraries which made the storage that the docker container required only about 70 MB (compared to a normal crystal image which requires around 480 MB).

I personally would like to suggest having docker images that can run Crystal binaries (have the necessary libraries) but not be able to compile Crystal.

Each project have different dependencies, even two projects in pure Crystal can have different dependencies, e.g. if you use YAML your project need to link to libyaml, if not you don’t.

So I guess that the current crystal image + using docker multi stage builds + knowing about your project dependencies is enough.


This is the process I use to build most of my container images for Crystal apps. It was written by a former coworker of mine, and I’ve only made a few changes to my own process:

  • my build stage uses official Alpine images for Crystal or 84codes’s Alpine images for Crystal when I need ARM support — I run some of my stuff on an ARM cluster on GKE
  • I use FROM scratch for the deployable container so my binaries are the only thing in the image (caveats apply) since I compile my apps statically and frequently don’t need an OS on the container

When the deployable container contains only one binary your container images are usually 6-15MB compressed, and even slightly smaller for ARM builds. One example is my rails-app Kubernetes operator, which contains just the operator binary and is only 6.15MB on Intel/5.89MB on ARM.

If you need things like tzdata and ca-certificates, it’s better to stick with alpine images instead of scratch as the base for your deployable container, but even then my largest container image is 40MB which includes:

  • 3 binaries — web server, background-job runner, and the DB-migration tool
  • the DB migration query files — interro uses .sql files so migrations don’t need to be compiled in
  • all the source code including shard dependencies and the Crystal stdlib source code to show code as context in Bugsnag, though hilariously this is often unhelpful because LLVM is so good at inlining code that I usually only see 1-3 lines in the stacktrace in production