FROM --platform=$BUILDPLATFORM ghcr.io/crazy-max/osxcross:14.5-debian AS osxcross ######################################################################################################################## ### Build xx (original image: tonistiigi/xx) FROM --platform=$BUILDPLATFORM public.ecr.aws/docker/library/alpine:3.20 AS xx-build # v1.9.0 ENV XX_VERSION=a5592eab7a57895e8d385394ff12241bc65ecd50 RUN apk add -U --no-cache git RUN git clone https://github.com/tonistiigi/xx && \ cd xx && \ git checkout ${XX_VERSION} && \ mkdir -p /out && \ cp src/xx-* /out/ RUN cd /out && \ ln -s xx-cc /out/xx-clang && \ ln -s xx-cc /out/xx-clang++ && \ ln -s xx-cc /out/xx-c++ && \ ln -s xx-apt /out/xx-apt-get # xx mimics the original tonistiigi/xx image FROM scratch AS xx COPY --from=xx-build /out/ /usr/bin/ ######################################################################################################################## ### Build Navidrome UI FROM --platform=$BUILDPLATFORM public.ecr.aws/docker/library/node:lts-alpine AS ui WORKDIR /app # Install node dependencies COPY ui/package.json ui/package-lock.json ./ COPY ui/bin/ ./bin/ RUN npm ci # Build bundle COPY ui/ ./ RUN npm run build -- --outDir=/build FROM scratch AS ui-bundle COPY --from=ui /build /build ######################################################################################################################## ### Build Navidrome binary for Docker image (dynamic musl, enables native libwebp via dlopen) FROM --platform=$BUILDPLATFORM public.ecr.aws/docker/library/golang:1.26-alpine AS build-alpine COPY --from=xx / / ARG TARGETPLATFORM RUN apk add --no-cache clang lld file git RUN xx-apk add --no-cache gcc musl-dev zlib-dev RUN xx-verify --setup WORKDIR /workspace RUN --mount=type=bind,source=. \ --mount=type=cache,target=/root/.cache \ --mount=type=cache,target=/go/pkg/mod \ go mod download ARG GIT_SHA ARG GIT_TAG RUN --mount=type=bind,source=. \ --mount=from=ui,source=/build,target=./ui/build,ro \ --mount=type=cache,target=/root/.cache \ --mount=type=cache,target=/go/pkg/mod </dev/null || true # Only Darwin (macOS) requires clang (default), Windows requires gcc, everything else can use any compiler. # So let's use gcc for everything except Darwin. if [ "$(xx-info os)" != "darwin" ]; then export CC=$(xx-info)-gcc export CXX=$(xx-info)-g++ export LD_EXTRA="-extldflags '-static -latomic'" fi # GNU ld corrupts the R_ARM_IRELATIVE addends of libatomic's ifunc resolvers # (wrong address, Thumb bit lost) once .text outgrows the 16MB Thumb branch # range, making static arm binaries jump to garbage inside glibc's ifunc # resolution and crash before main() (issue #5738). Link 32-bit arm with LLD, # which emits correct addends. if [ "$(xx-info arch)" = "arm" ]; then export LD_EXTRA="-extldflags '-static -latomic -fuse-ld=lld'" fi if [ "$(xx-info os)" = "windows" ]; then export EXT=".exe" fi BUILD_TAGS=$(./release/build-tags.sh) go build -tags="${BUILD_TAGS}" -ldflags="${LD_EXTRA} -w -s \ -X github.com/navidrome/navidrome/consts.gitSha=${GIT_SHA} \ -X github.com/navidrome/navidrome/consts.gitTag=${GIT_TAG}" \ -o /out/navidrome${EXT} . # Fail the build if native libwebp (purego) leaked into a 32-bit binary (issue #5738). ./release/verify-binary.sh /out/navidrome* EOT # Verify if the binary was built for the correct platform and it is statically linked RUN xx-verify --static /out/navidrome* FROM scratch AS binary COPY --from=build /out / ######################################################################################################################## ### Build Final Image FROM public.ecr.aws/docker/library/alpine:3.20 AS final LABEL maintainer="deluan@navidrome.org" LABEL org.opencontainers.image.source="https://github.com/navidrome/navidrome" # Install runtime dependencies # - libwebp + symlinks: enables native WebP encoding via purego/dlopen RUN apk add -U --no-cache ffmpeg mpv sqlite libwebp libwebpdemux libwebpmux && \ for lib in libwebp libwebpdemux libwebpmux; do \ target=$(ls /usr/lib/$lib.so.* 2>/dev/null | head -1) && \ [ -n "$target" ] && ln -sf "$target" /usr/lib/$lib.so; \ done # Copy navidrome binary (musl build for Docker, enables native libwebp) COPY --from=build-alpine /out/navidrome /app/ VOLUME ["/data", "/music"] ENV ND_MUSICFOLDER=/music ENV ND_DATAFOLDER=/data ENV ND_CONFIGFILE=/data/navidrome.toml ENV ND_PORT=4533 RUN touch /.nddockerenv EXPOSE ${ND_PORT} WORKDIR /app ENV PATH="/app:${PATH}" ENTRYPOINT ["/app/navidrome"]