FROM node:26-slim AS base WORKDIR /app FROM base AS pnpm COPY package.json pnpm-lock.yaml pnpm-workspace.yaml ./ # Node 26 ships without corepack, so install the pnpm that package.json pins. RUN npm install -g "$(node -p "require('./package.json').packageManager.split('+')[0]")" FROM pnpm AS deps RUN pnpm install --frozen-lockfile --prod # Build client assets (needs devDependencies for vp) FROM pnpm AS build RUN apt-get update && apt-get install -y ca-certificates && rm -rf /var/lib/apt/lists/* RUN pnpm install --frozen-lockfile COPY . . RUN pnpm build # Runtime FROM base COPY . . COPY --from=deps /app/node_modules ./node_modules COPY --from=build /app/dist ./dist EXPOSE 5176 # The webstorage flag silences Node 26's localStorage warning; the server never # uses it, but a bundled dependency probes for it at boot. # `exec` matters: without it the server runs as a child of this shell, which does # not forward SIGTERM, so the graceful shutdown in lib/runtime/shutdown.ts never runs. CMD ["sh", "-c", "node lib/db/migrate.ts && exec node --no-experimental-webstorage dist/server.js"]