# syntax=docker/dockerfile:1 FROM node:22-alpine AS builder WORKDIR /app # Install dependencies in a separate layer so it is cached as long as # package-lock.json does not change. The npm cache mount persists the # ~/.npm cache across builds so packages are not re-downloaded. COPY package.json package-lock.json ./ RUN --mount=type=cache,target=/root/.npm \ npm ci COPY . . # Build-time version info — injected by docker-compose or CI via --build-arg. ARG BUILD_VERSION=dev ARG BUILD_COMMIT=unknown ARG BUILD_TIME=unknown # Expose as PUBLIC_ env vars so SvelteKit's $env/dynamic/public can read them. ENV PUBLIC_BUILD_VERSION=$BUILD_VERSION ENV PUBLIC_BUILD_COMMIT=$BUILD_COMMIT ENV PUBLIC_BUILD_TIME=$BUILD_TIME RUN npm run build # ── Runtime image ────────────────────────────────────────────────────────────── # adapter-node bundles most server-side code, but packages with dynamic # requires or native bindings (e.g. ioredis) are not inlined by Rollup and # must be present in node_modules at runtime. We install only production # deps (no devDependencies) to keep the image small. FROM node:22-alpine WORKDIR /app COPY --from=builder /app/build ./build COPY --from=builder /app/package.json ./package.json COPY --from=builder /app/package-lock.json ./package-lock.json RUN --mount=type=cache,target=/root/.npm \ npm ci --omit=dev ENV NODE_ENV=production ENV PORT=3000 ENV HOST=0.0.0.0 # Carry build-time metadata into the runtime image so the UI footer can # display the version, commit SHA, and build timestamp. # These must be re-declared after the second FROM — ARG values do not # cross stage boundaries, but ENV values set here persist at runtime. ARG BUILD_VERSION=dev ARG BUILD_COMMIT=unknown ARG BUILD_TIME=unknown ENV PUBLIC_BUILD_VERSION=$BUILD_VERSION ENV PUBLIC_BUILD_COMMIT=$BUILD_COMMIT ENV PUBLIC_BUILD_TIME=$BUILD_TIME EXPOSE $PORT CMD ["node", "build"]