Michael Trojanek (relativkreativ) — Bootstrapper and creator of things

This article was published on February 4th 2019 and received its last update on February 20th 2019. It takes about 3 minutes to read.

How to compile a Crystal project with static linking

Compiling a Crystal project with static linking allows you to easily distribute it as a single file without any dependencies. Unfortunately, right now it is only possible on Alpine Linux machines. I'll show you two ways to do it.

If you follow me on Twitter, my love for Crystal will not have escaped your notice.

Why I am such a fan will be topic for another article, but being able to compile a Crystal project to a single binary is — besides its syntax and ideology closely resembling Ruby's — clearly the number one reason.

Dynamic linking

Crystal compiles with dynamic linking per default. This is no problem most of the time — compiling a Crystal project on CentOS, copying the resulting binary to a Ubuntu mache and running it there simply works. That's because they share the same dynamic libraries Crystal links to on compilation.

Things get a little trickier once you want to run it on a machine which does not provide the libraries your program depends on. Or if you want to package your compiled Crystal project into an RPM: The rpmbuild command throws a dependency issue which is impossible to resolve at the moment.

This is where static linking comes into play.

The state of static linking

The Crystal docs have sad news on static linking currently:

Static linking using the --static compiler flag is currently only supported on Alpine Linux. The easiest way to do this is to compile your application inside an Alpine Linux Docker container.

Use Docker

If you use Docker for development, this is indeed true. To statically compile your Crystal project, you can use this Docker command:

  docker run --rm -it -v $PWD:/app -w /app durosoft/crystal-alpine crystal build src/FILE.cr -o bin/FILE --release --static

This command uses durosoft's crystal-alpine container (which features tags for every Crystal build since 0.25.0, should you need a specific version), mounts the current directory to /app and statically compiles the project into a binary in the bin directory.

Use Vagrant

Firing up a Docker container for the sole purpose of compiling your project only pays if you already use Docker in your workflow.

If you are one of many developers who don't, you can either build your own Alpine Linux virtual machine (which is even more work) or simply use my prebuilt Vagrant box.

After creating a Vagrantfile with vagrant init relativkreativ/alpine-3 and running vagrant up there is a little work left to do before we can compile our Crystal project:

  • Install Crystal
    Run apk add crystal shards as root to install all packages related to Crystal. These packages are served from the community repository (which is already enabled in /etc/apk/repositories for my box).
  • Install development headers
    Pull in the correct libc with apk add libc-dev (which is a meta package which does just that).

Once this is done, you can compile your Crystal project with crystal build src/FILE.cr -o bin/FILE --release --static (the same command as above).

This article refers to Crystal 0.27.1.

Get in the loop

Join my email list to get new articles delivered straight to your inbox and discounts on my products.

No spam — guaranteed.

You can unsubscribe at any time.

Got it, thanks a lot!

Please check your emails for the confirmation request I just sent you. Once you clicked the link therein, you will no longer see these signup forms.