How can I produce a Crystal executable with no dependencies?

In Linux, you can list the shared libraries that an executable needs using the ldd command. In OSX, otool -L could be used for the same purpose.

Normally, the linker will use shared libraries while building the executable if it can find them. So, what you need to do, is force the linker to use static libraries instead. (In the future we might add a flag to the compiler to force this choice)

You should find some of these static libraries in /opt/crystal/embedded/lib. We use these to generate a portable Crystal compiler.

In order to use these libraries you can run:

$ LIBRARY_PATH=/opt/crystal/embedded/lib crystal build

That should prefer libraries available in that directory before considering others installed in the standard locations.

Unfortunately, OpenSSL is not distributed with Crystal, so you must either copy or build a static version of libssl and libcrypto. It's a common library anyway, available in any Linux distribution.

Regarding libc, that's more tricky. We compile the Crystal binary for releases using old CentOS and Debian distributions to make it compatible with many more libc versions.

The Crystal docs mention that the --static compiler flag currently only works on Alpine Linux (and recommends using an Alpine Linux Docker container).

If you are not into Docker but happen to have Vagrant on your machine, you can use my Alpine box (relativkreativ/alpine) instead of going through the hassle of building your own.

In either case, static linking works on Alpine.

Just run the following steps:

  • Enable the community repository in /etc/apk/repositories
  • Run apk update the get the latest package list
  • Install Crystal with apk add crystal shards
  • Pull in the correct libc with apk add libc-dev (a meta-package which does just that)

Once this is done, you can compile your Crystal project with crystal build src/ -o bin/FILE --release --static.

Now having a Crystal binary with no dependencies allows you to easily distribute your project as RPM, for example (which is how I found this question in the first place).

I also summarised this in an article on my website.