FROM node:24-slim AS manifests WORKDIR /app RUN corepack enable COPY package.json pnpm-lock.yaml pnpm-workspace.yaml ./ COPY patches ./patches FROM manifests AS deps RUN corepack pnpm install --frozen-lockfile # adapter-bun bundles devDependencies into build/, so the runtime only needs these. # pnpm's own state files carry the install time, and nothing at runtime reads them FROM manifests AS prod-deps RUN corepack pnpm install --frozen-lockfile --prod \ && rm node_modules/.modules.yaml node_modules/.pnpm-workspace-state-v1.json FROM node:24-slim AS build WORKDIR /app RUN corepack enable COPY --from=oven/bun:1.4.4-slim /usr/local/bin/bun /usr/local/bin/bun COPY --from=deps /app/node_modules ./node_modules COPY . . # vite.config.ts falls back to the prod oauth client when these are unset ARG VITE_OAUTH_CLIENT_ID ARG VITE_OAUTH_REDIRECT_URI ARG APP_VERSION RUN corepack pnpm run build FROM oven/bun:1.4.4-slim WORKDIR /app ENV NODE_ENV=production ENV HOST=0.0.0.0 ENV PORT=3000 ENV BUN_FEATURE_FLAG_EXPERIMENTAL_HTTP2_CLIENT=1 # the 2.0 default peaks above node's memory, 1.6 doesn't and still beats node on cpu # vs node on prod pages: 1.41x req/s, 9.9 vs 14.0 ms cpu per page, 359 vs 379 MB peak ENV BUN_JSC_smallHeapGrowthFactor=1.6 COPY --from=build /app/build ./build COPY --from=build /app/package.json ./package.json COPY --from=prod-deps /app/node_modules ./node_modules EXPOSE 3000 CMD ["bun", "build/index.js"]