From 6127e7b7817d62e51d520a1bedc0d78c52ee2541 Mon Sep 17 00:00:00 2001 From: "khalid@traclabs.com" Date: Sat, 23 May 2026 03:42:05 -0500 Subject: [PATCH] Fix docker file --- Dockerfile | 84 +++++++++++++++++++++++++++++++++++++++++++++++------- 1 file changed, 74 insertions(+), 10 deletions(-) diff --git a/Dockerfile b/Dockerfile index 3e8fb4c..6593adb 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,40 +1,104 @@ +# Multi-stage Dockerfile. +# +# Why two stages +# ────────────── +# `canvas` is a native-binding npm package. It needs Cairo/Pango/etc. +# at runtime, and python3+make+g++ at install-time to compile its +# native bindings (no prebuilt binary covers the alpine musl+napi+linux +# combination at this canvas version; prebuild-install falls back to +# node-gyp, which needs the compile toolchain). +# +# The naive single-stage approach would either: +# (a) keep python3+make+g++ in the final image — inflates the image +# by ~200MB of build tools nothing uses at runtime, or +# (b) try to install --omit=dev in a runtime-only image — fails +# because canvas tries to compile and has no python (this was +# the bug fixed in this revision). +# +# Two-stage solves both: install + compile + prune dev deps in the +# builder (which has the toolchain), then COPY the resulting +# node_modules into a slim runtime that only carries the C runtime +# libs canvas needs at execution time. + +# ───────────────────────────────────────────────────────────────── +# Stage 1: builder +# Installs all deps (including dev), compiles native bindings, +# builds the Vite bundle, then prunes dev deps so node_modules is +# production-ready before we copy it. +# ───────────────────────────────────────────────────────────────── FROM node:20-alpine AS builder -RUN apk add --no-cache cairo-dev pango-dev libjpeg-turbo-dev giflib-dev librsvg-dev pixman-dev python3 make g++ +# Native-binding build prerequisites: +# • python3 / make / g++ — node-gyp toolchain for compiling canvas +# • cairo-dev / pango-dev / etc. — headers canvas links against +# These stay in the builder stage only; the runtime stage carries +# the matching runtime libraries (no -dev suffix) without the +# toolchain. +RUN apk add --no-cache \ + cairo-dev pango-dev libjpeg-turbo-dev giflib-dev librsvg-dev pixman-dev \ + python3 make g++ WORKDIR /app + +# Copy package manifests first so docker can cache the npm install +# layer when only application source changes. COPY package*.json ./ RUN npm install + COPY . . -# Pull Google Fonts into ./fonts/ so they end up in the runtime image. If the -# build host has no network this step will produce missing-variant warnings; -# the build still succeeds and the server falls back to system fonts. +# Pull Google Fonts into ./fonts/ so they end up in the runtime image. +# If the build host has no network this step will produce missing-variant +# warnings; the build still succeeds and the server falls back to system +# fonts. RUN npm run fetch-fonts || echo "Font fetch had warnings; continuing with whatever was downloaded." RUN npm run build +# Drop dev dependencies (vitest, playwright, eslint, etc.) so the +# node_modules we copy to runtime is production-only. This is the +# step that was missing before — without it, the runtime stage had +# to re-run `npm install` from scratch, which would re-trigger +# canvas's native compile in an image that doesn't have the +# toolchain. By pruning here, the runtime stage just copies the +# already-compiled directory. +RUN npm prune --production + +# ───────────────────────────────────────────────────────────────── +# Stage 2: runtime +# Carries only the runtime C libraries canvas links against plus +# the fonts the export pipeline needs. node_modules is copied +# whole from the builder; no `npm install` happens here. +# ───────────────────────────────────────────────────────────────── FROM node:20-alpine # Cairo + Pango are required by node-canvas at runtime. -# The font packages cover the proprietary template families (Impact, Times, -# Courier, Arial) via free equivalents that fontconfig will alias, plus emoji -# rendering for sticker exports. +# The font packages cover the proprietary template families (Impact, +# Times, Courier, Arial) via free equivalents that fontconfig will +# alias, plus emoji rendering for sticker exports. RUN apk add --no-cache \ cairo pango libjpeg-turbo giflib librsvg pixman \ ttf-liberation ttf-dejavu font-noto font-noto-emoji \ fontconfig WORKDIR /app -COPY package*.json ./ -RUN npm install --omit=dev +# Bring the production node_modules over from the builder rather +# than installing fresh. This carries the already-compiled canvas +# .node binary without needing python/make/g++ in this stage. +COPY --from=builder /app/node_modules ./node_modules +COPY --from=builder /app/package*.json ./ + +# Application files. server.js is the entry point; dist/ is the +# Vite build output served as static; fonts/ is the result of +# fetch-fonts above. COPY server.js ./ COPY --from=builder /app/dist ./dist COPY --from=builder /app/fonts ./fonts RUN mkdir -p /app/uploads /app/exports -# Refresh fontconfig's cache so the newly-copied custom fonts are discoverable. +# Refresh fontconfig's cache so the newly-copied custom fonts are +# discoverable. RUN fc-cache -f /app/fonts || true HEALTHCHECK --interval=30s --timeout=10s --start-period=5s --retries=3 \