Running Elixir Graphics & ML tools on FreeBSD
Monday, 25 May 2026
Most of the interesting Elixir libraries for image processing and ML have one thing in common: they’re NIFs wrapping a native C/C++ library. Vix wraps libvips, Nx wraps XLA, Bumblebee reaches into ONNX runtime, Image sits on top of Vix, and so on. On Linux & macOS, the Hex ecosystem has converged on shipping precompiled NIFs so end users don’t have to install a C toolchain. I am not a fan of downloading large swathes of pre-compiled C libraries from random Github repositories.
So, this post walks through getting Vix, Image, and eVision to compile and run on FreeBSD 15.1-RELEASE, failure modes you hit on the default builds, and how to avoid them.
Vix
We’ll focus on Vix first, since it’s the foundation: Image is built on top of it, and eVision wraps OpenCV with the same precompiled-NIF assumptions we’re about to break.
Prerequisites
Everything below assumes a working FreeBSD build environment and the native libraries the three projects wrap. On 15.1-RELEASE this is one command:
# pkg install -yr FreeBSD-ports \
graphics/vips \
math/openblas \
graphics/opencv \
lang/elixir-devel \
lang/erlang-runtime29That gets us libvips (for Vix and Image), OpenBLAS (which OpenCV and most of the Nx-adjacent stack want for linear algebra), OpenCV itself (for eVision), a recent Elixir, and Erlang/OTP 29.
The default build, and where it goes wrong
Vix already needs to be told explicitly to use CC=cc instead of
assuming gcc everywhere.
$ env CC=cc mix do deps.get --all + deps.compile && \
env CC=cc mix test --trace --stale
Looking at c_src/Makefile shows that Vix actually has three build
modes, controlled by VIX_COMPILATION_MODE:
VIX_COMPILATION_MODE ?= PRECOMPILED_NIF_AND_LIBVIPS
VALID_COMPILATION_MODES := PRECOMPILED_NIF_AND_LIBVIPS \
PRECOMPILED_LIBVIPS \
PLATFORM_PROVIDED_LIBVIPSPRECOMPILED_NIF_AND_LIBVIPS— fetch a prebuilt libvips tarball fromsharp-libvips, build only the small NIF shim against it.PRECOMPILED_LIBVIPS— same idea but always rebuilds the NIF.PLATFORM_PROVIDED_LIBVIPS— trustpkg-configto find a libvips the system already has.
That third mode is exactly what we want on FreeBSD, because the ports tree already has graphics/vips and friends.
$ env CC=cc VIX_COMPILATION_MODE=PLATFORM_PROVIDED_LIBVIPS \
mix compile --force
CC pipe.c
CC utils.c
CC vips_boxed.c
CC vips_foreign.c
CC vips_image.c
CC vips_interpolate.c
CC vips_operation.c
CC vix.c
CC g_boxed.c
CC g_object.c
CC g_param_spec.c
CC g_type.c
CC g_value.c
LD vix.so
Build complete: /tmp/vix/_build/dev/lib/vix/priv/vix.soAnd then:
$ env CC=cc VIX_COMPILATION_MODE=PLATFORM_PROVIDED_LIBVIPS \
mix test --trace --stale
...
Finished in 0.4 seconds (0.4s async, 0.00s sync)
Result: 271 passed (151 doctests, 120 tests)