Skip to content

Static Linking

Crystal supports static linking, i.e. it can link a binary with static libraries so that these libraries don't need to be available as runtime dependencies.

Static linking can be enabled using the --static compiler flag. See the usage instructions in the language reference.

When --static is given, linking static libraries is enabled, but it's not exclusive. The produced binary won't be fully static linked if the dynamic version of a library is higher in the compiler's library lookup chain than the static variant (or if the static library is entirely missing). In order to build a static binary you need to make sure that static versions of the linked libraries are available and the compiler can find them.

The compiler uses the CRYSTAL_LIBRARY_PATH environment variable as a first lookup destination for static and dynamic libraries that are to be linked. This can be used to provide static versions of libraries that are also available as dynamic libraries.

Not all libraries work well with being statically linked, so there may be some issues. openssl for example is known for complications, as well as glibc (see Fully Static Linking).

Some package managers provide specific packages for static libraries, where foo provides the dynamic library and foo-static for example provides the static library. Sometimes static libraries are also included in development packages.

Fully Static Linking

A fully statically linked program has no dynamic library dependencies at all. Prominent examples of fully statically linked Crystal programs are the crystal and shards binaries from the official distribution packages.

In order to link a program fully statically, all dependencies need to be available as static libraries at compiler time. This can be tricky sometimes, especially with common libc libraries.

Linux

glibc

glibc is the most common libc implementation on Linux systems. Unfortunately, it doesn't play nicely with static linking and it's highly discouraged.

Instead, static linking against musl-libc is the recommended option on Linux. Since it's statically linked, a binary linked against musl-libc will also run on a glibc system. That's the entire point of it.

musl-libc

musl-libc is a clean, efficient libc implementation with excellent static linking support.

The recommended way to build a statically linked Crystal program is Alpine Linux, a minimal Linux distribution based on musl-libc.

Official Docker Images based on Alpine Linux are available on Docker Hub at crystallang/crystal. The latest release is tagged as crystallang/crystal:latest-alpine. The Dockerfile source is available at crystal-lang/distribution-scripts.

With pre-installed crystal compiler, shards, and static libraries of all of stdlib's dependencies these Docker images allow to easily build static Crystal binaries even from glibc-based systems. The official Crystal compiler builds for Linux are created using these images.

Here's an example how the Docker image can be used to build a statically linked Hello World program:

$ echo 'puts "Hello World!"' > hello-world.cr
$ docker run --rm -it -v $(pwd):/workspace -w /workspace crystallang/crystal:latest-alpine \
    crystal build hello-world.cr --static
$ ./hello-world
Hello World!
$ ldd hello-world
        statically linked

Alpineā€™s package manager APK is als easy to work with to install static libraries. Available packages can be found at pkgs.alpinelinux.org.

macOS

macOS doesn't officially support fully static linking because the required system libraries are not available as static libraries.