Nomad on Alpine Linux with MUSL: lightweight but not trivial

Nomad on Alpine Linux with MUSL: lightweight but not trivial
By Matthias Petermann / on 03.11.2025

Introduction

Nomad by HashiCorp is a lightweight orchestration and scheduling tool for containers, virtual machines, and simple binaries. Unlike Kubernetes, Nomad does not require extensive infrastructure. It runs smoothly on a single node and impresses with its simplicity - especially in smaller setups, home labs or edge environments.

At its core, Nomad consists of a single binary that can act as both a server and a client. This makes it ideal for compact systems – such as an Alpine Linux server.

ℹ️ Nomad in brief
  • Language: Go
  • Task: Orchestration and scheduling
  • Architecture: Simple, binary deployment
  • Alternatives: Kubernetes, Docker Swarm

Alpine Linux & MUSL – minimalism with pitfalls

Alpine Linux stands for minimalism. The distribution is based on:

  • musl libc instead of glibc
  • busybox instead of GNU Coreutils

The result: an extremely slim, fast and secure system – ideal for container environments and resource-poor hardware.

But this minimalism has side effects. Many applications built against glibc will not work easily with musl. It becomes particularly problematic if you compile on a glibc-based system and then run the binary on Alpine.

⚠️ glibc ≠ musl
MUSL is a standalone implementation of the C standard library - slim, clean and POSIX compliant. glibc, on the other hand, is the de facto standard of most Linux distributions. A binary linked against glibc will therefore not automatically run under musl.

The problem: built on Xubuntu, failed on Alpine

This is exactly the trap I fell into.

I wanted to build Nomad from source on my Xubuntu workstation (glibc) - and then run it on my Alpine-based home server.

The first attempt

git clone https://github.com/hashicorp/nomad.git
cd nomad
make release TARGETS=linux_amd64

The build went through without errors. The finished binary was in the pkg/linux_amd64/ directory:

pkg/linux_amd64/nomad

But after copying it to the Alpine server, disillusionment followed:

./nomad
-sh: ./nomad: not found

The file existed but was not executable. A classic indication of a linking problem.

Diagnosis with ldd

A short test brought certainty:

ldd nomad

Result:

        /lib64/ld-linux-x86-64.so.2 (0x7fa9060cf000)
        libc.so.6 => /lib64/ld-linux-x86-64.so.2 (0x7fa9060cf000)
Error relocating nomad: __snprintf_chk: symbol not found
Error relocating nomad: __vfprintf_chk: symbol not found
Error relocating nomad: __pthread_unregister_cancel: symbol not found
Error relocating nomad: __register_atfork: symbol not found
Error relocating nomad: __asprintf_chk: symbol not found
Error relocating nomad: __vdprintf_chk: symbol not found
Error relocating nomad: __isoc23_strtol: symbol not found
Error relocating nomad: __vasprintf_chk: symbol not found
Error relocating nomad: __vsnprintf_chk: symbol not found
Error relocating nomad: __strncpy_chk: symbol not found
Error relocating nomad: __longjmp_chk: symbol not found
Error relocating nomad: __dprintf_chk: symbol not found
Error relocating nomad: __fprintf_chk: symbol not found
Error relocating nomad: __pthread_register_cancel: symbol not found

The output clearly showed: The binary was linked against glibc - and musl simply couldn’t provide the required symbols.

🔒 Lesson
A Linux binary is not automatically universally executable. The difference often lies in the C standard library.
💡 Further: Nomad on NetBSD
If you are interested in cross-platform ports, see my post “Proof of Concept: Porting Nomad to NetBSD” a deeper analysis of Nomad’s portability beyond Linux.

The solution: Build directly under Alpine

The most elegant approach: Build Nomad natively with musl - i.e. directly in an Alpine environment.

A compact container that contains all the necessary tools is sufficient. The following container file lays the foundation:

# syntax=docker/dockerfile:1
FROM alpine:3.22 AS builder

# --- Build dependencies ---
RUN apk add --no-cache \
    bash \
    build-base \
    git \
    curl \
    ca-certificates \
    make \
    unzip \
    tar \
    linux-headers \
    zip

# --- Install Go ---
ENV GOLANG_VERSION=1.25.3
RUN curl -fsSL https://go.dev/dl/go${GOLANG_VERSION}.linux-amd64.tar.gz | tar -C /usr/local -xz
ENV PATH="/usr/local/go/bin:${PATH}"

# --- Build environment ---
WORKDIR /src
COPY . .

ENV CGO_ENABLED=0 \
    GOOS=linux \
    GOARCH=amd64 \
    GOPATH=/root/go \
    PATH="/root/go/bin:/usr/local/go/bin:${PATH}"

# --- Build Nomad ---
RUN make release TARGETS=linux_amd64

# --- Post-build export step ---
# Wenn /out gemountet ist, wird das Ergebnis direkt dort abgelegt
RUN mkdir -p /out && \
    cp pkg/linux_amd64/nomad /out/nomad && \
    cp pkg/linux_amd64.zip /out/linux_amd64.zip

# --- Runtime behavior: export results and exit ---
CMD ["/bin/sh", "-c", "echo '✅ Build complete. Files are in /out'; ls -lh /out"]

Compile and export

mkdir dist
podman build -v "$(pwd)/dist:/out" -t nomad-musl-builder -f Containerfile

After successful build the files are ready:

ls dist
linux_amd64.zip  nomad

A quick check on the target system confirms success:

ldd nomad

Output:

        /lib/ld-musl-x86_64.so.1 (0x7fc7a84ff000)
        libc.musl-x86_64.so.1 => /lib/ld-musl-x86_64.so.1 (0x7fc7a84ff000)

The binary is now musl-native – and runs without any problems.

Test run

./nomad agent -dev -bind=0.0.0.0

In the browser under http://<ip-des-servers>:4646 The usual Nomad interface appears - this time without error messages.

🏗️ Advantages of container build
  • No cross-compiling necessary
  • Reproducible builds
  • Correct target for libc guaranteed
  • Clean separation of build and runtime environments

Conclusion

Alpine Linux is an excellent foundation for lean systems - but its musl-based architecture demands attention when building software. Nomad itself is written in Go and can therefore be compiled natively for musl with little effort.

A dedicated container build ensures reproducible results and avoids typical pitfalls.

Finding: If you want to deploy on Alpine, you should build there too.

“Minimalism only works if you understand what’s missing.”


Image credit: The Nomad logo is a registered trademark of HashiCorp, Inc. Use in editorial reporting in accordance with HashiCorp Trademark Guidelines. The Alpine Linux logo is the property of the Alpine Linux Development Team. Use within the context of editorial reporting. Tux, the Linux mascot, was created by Larry Ewing (using GIMP, 1996). Use within the context of editorial reporting.