From 4f00496127bbb1bb4f4452f9bd864cfce9b60ae3 Mon Sep 17 00:00:00 2001 From: Claude Date: Sun, 25 Oct 2026 10:04:48 +0000 Subject: [PATCH] fix(build): install server deps natively, not under QEMU (fixes stalled :latest builds) npm ci for linux/arm64 ran under QEMU emulation and intermittently crashed with "qemu: uncaught target signal 4 (Illegal instruction)"; buildx then hung instead of failing, stalling the :latest builds of v1.8.0 and v1.9.0. Nothing in the server deps needs compiling for the target CPU (better-sqlite3 bundles prebuilt binaries for every platform and picks one at runtime), so install them once on the build machine with --ignore-scripts and copy them into both images. Drops the python3/make/g++ toolchain stage. Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_01KiL5R3bRpvGk6QP3E21eHF --- server/Dockerfile | 24 +++++++++++++++++------- 1 file changed, 17 insertions(+), 7 deletions(-) diff --git a/server/Dockerfile b/server/Dockerfile index 8dbfe7b..182457c 100644 --- a/server/Dockerfile +++ b/server/Dockerfile @@ -14,15 +14,25 @@ COPY client/ ./ RUN npm run build # ---- Stage 2: server dependencies ---- -# better-sqlite3 is a native module with no musl (Alpine) prebuilt binary, so -# it must be compiled from source here — which needs a toolchain. Doing it in a -# dedicated stage keeps python3/make/g++ out of the final image. This stage -# runs on the TARGET platform, so the compiled binary matches the runtime arch. -FROM node:22-alpine AS server-deps -RUN apk add --no-cache python3 make g++ +# Built on the BUILD machine's own CPU (like the client), not under QEMU +# emulation: `npm ci` for linux/arm64 under QEMU intermittently died with +# "qemu: uncaught target signal 4 (Illegal instruction)", and buildx then hung +# instead of failing (it stalled the `:latest` builds of v1.8.0 and v1.9.0). +# +# Nothing here needs compiling for the target CPU, so the result is the same +# for every platform: the deps are plain JS, and better-sqlite3 ships prebuilt +# binaries for each platform inside the package and picks the right one at +# runtime (prebuilds/linuxmusl-.node). --ignore-scripts skips its +# redundant node-gyp step, ssh2/cpu-features' optional native speedups (only +# used for Docker-over-SSH, which DockPull doesn't use; they fall back to JS), +# and protobufjs's warning-only postinstall. No compiler toolchain needed. +FROM --platform=$BUILDPLATFORM node:22-alpine AS server-deps WORKDIR /app COPY server/package*.json ./ -RUN npm ci --omit=dev +RUN npm ci --omit=dev --ignore-scripts \ + && find node_modules/better-sqlite3/prebuilds -type f ! -name 'linuxmusl-*' -delete \ + && test -f node_modules/better-sqlite3/prebuilds/linuxmusl-x64.node \ + && test -f node_modules/better-sqlite3/prebuilds/linuxmusl-arm64.node # ---- Stage 3: server runtime ---- FROM node:22-alpine