From e53e60d39da2a6cd014a0253e64d3f03da872369 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Deluan=20Quint=C3=A3o?= Date: Mon, 13 Apr 2026 13:30:05 -0400 Subject: [PATCH] feat(artwork): enable native libwebp encoding in Docker image (#5350) * feat(docker): add musl build stage for native libwebp support Add a new build-alpine stage using Alpine/musl with xx cross-compilation, producing a dynamically-linked musl binary for the Docker image. The runtime image now installs libwebp, libwebpdemux, and libwebpmux and creates .so symlinks so gen2brain/webp can detect native libwebp via purego/dlopen at startup and use it automatically. The existing Debian/glibc 'build' stage is kept for standalone binary distribution (darwin, windows, and glibc linux binaries); the Docker image now ships the musl build from build-alpine instead. * fix(docker): use dynamic symlinks for libwebp libraries Avoid hardcoding SONAME versions (.so.7, .so.2, .so.3) which break on Alpine version bumps. Also fix misleading comment: the musl build is dynamic (required for purego dlopen), not static. * feat(docker): enable WebP encoding in Docker environment Signed-off-by: Deluan * fix(docker): pin build-alpine stage to Go 1.25 to match base stage Align the new build-alpine stage with the existing glibc 'base' stage, both pinned to Go 1.25. Bumping build-alpine independently would create a version skew between the Docker image binary and the standalone binaries, which should be avoided unless there is a specific reason. * fix(docker): harden build-alpine stage (musl pin, -latomic, dynamic-link check) Address review feedback on the build-alpine stage: - Pin Go builder to golang:1.25-alpine3.20 so the musl version used at build time matches the alpine:3.20 runtime image, eliminating any potential musl ABI skew between builder and runtime. - Add -extldflags '-latomic' so SQLite's 64-bit atomics resolve when cross-compiling for 32-bit arm targets (arm/v6, arm/v7). - Add a build-time check that the produced binary is dynamically linked (using 'file' from Alpine), failing the build if it is not. A fully-static binary cannot dlopen libwebp and would silently fall back to the WASM encoder, defeating the whole point of this stage. * fix(docker): revert to unpinned golang:1.25-alpine builder The golang:1.25-alpine3.20 tag suggested during review does not exist on public.ecr.aws (only 3.21, 3.22, 3.23, and unpinned 'alpine' are published). Revert to the unpinned 'golang:1.25-alpine' tag so the Docker build can resolve the base image. This means the builder's Alpine version can drift relative to the alpine:3.20 runtime, but in practice musl's backward compatibility covers this for Navidrome's small dlopen surface (a few libwebp symbols, no direct libc calls from the dlopen path). If a skew ever manifests, we can pin both builder and runtime to the same specific Alpine release in a follow-up. --------- Signed-off-by: Deluan --- Dockerfile | 55 +++++++++++++++++++++++++++++++++++++++++++++++++----- 1 file changed, 50 insertions(+), 5 deletions(-) diff --git a/Dockerfile b/Dockerfile index f6ea14ff3..66243f84c 100644 --- a/Dockerfile +++ b/Dockerfile @@ -42,7 +42,46 @@ FROM scratch AS ui-bundle COPY --from=ui /build /build ######################################################################################################################## -### Build Navidrome binary +### Build Navidrome binary for Docker image (dynamic musl, enables native libwebp via dlopen) +FROM --platform=$BUILDPLATFORM public.ecr.aws/docker/library/golang:1.25-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 | head -1) && \ + [ -n "$target" ] && ln -sf "$target" /usr/lib/$lib.so; \ + done -# Copy navidrome binary -COPY --from=build /out/navidrome /app/ +# 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 +ENV ND_ENABLEWEBPENCODING=true RUN touch /.nddockerenv EXPOSE ${ND_PORT}