Files
civitai__civitai/Dockerfile
T

110 lines
4.7 KiB
Docker
Raw Normal View History

2023-01-24 17:10:25 -07:00
##### DEPENDENCIES
FROM node:20-alpine3.20 AS deps
2023-02-21 09:53:06 -07:00
RUN apk add --no-cache libc6-compat
2023-01-24 17:10:25 -07:00
WORKDIR /app
# Enable corepack for pnpm
RUN corepack enable && corepack prepare pnpm@10.28.1 --activate
# Copy Prisma schema for client generation (postinstall generates schema.prisma from this)
COPY prisma/schema.full.prisma ./prisma/
# Install dependencies — lockfile and scripts rarely change, so they go first.
# package.json changes on every version bump but pnpm only needs it for the
# workspace root name; the store cache mount lets pnpm reuse downloaded packages
# even when this layer is invalidated.
COPY pnpm-lock.yaml package.json ./
COPY scripts ./scripts
COPY patches ./patches
RUN --mount=type=cache,id=pnpm-store,target=/root/.local/share/pnpm/store \
pnpm install --frozen-lockfile
2023-01-24 17:10:25 -07:00
##### BUILDER
FROM node:20-alpine3.20 AS builder
2023-01-24 17:10:25 -07:00
ARG NEXT_PUBLIC_IMAGE_LOCATION
ARG NEXT_PUBLIC_CONTENT_DECTECTION_LOCATION
ARG NEXT_PUBLIC_MAINTENANCE_MODE
WORKDIR /app
# Enable corepack for pnpm
RUN corepack enable && corepack prepare pnpm@10.28.1 --activate
2023-01-24 17:10:25 -07:00
COPY --from=deps /app/node_modules ./node_modules
COPY . .
# Restore generated schema.prisma from deps (COPY . . overwrites it with source which doesn't have it)
COPY --from=deps /app/prisma/schema.prisma ./prisma/schema.prisma
2023-01-24 17:10:25 -07:00
2025-09-04 12:20:30 -05:00
ENV NEXT_TELEMETRY_DISABLED=1
2023-01-24 17:10:25 -07:00
# Node heap for the Next.js build. Default raised 6144 -> 8192: a cold build
# (no warm .next/cache) peaks higher than an incremental one and OOMs at 6 GB on
# newer commits. Build-arg so a builder with more memory can raise it further.
ARG NODE_BUILD_MEM=8192
RUN --mount=type=cache,target=/app/.next/cache \
SKIP_ENV_VALIDATION=1 IS_BUILD=true NODE_OPTIONS="--max_old_space_size=${NODE_BUILD_MEM}" pnpm run build
2023-01-24 17:10:25 -07:00
feat(build): ship server source maps for prod CPU-profile de-minification (#2460) * feat(build): ship server source maps for prod CPU-profile de-minification Prod pods capture V8 .cpuprofiles to find event-loop blockers, but the standalone image shipped zero server .js.map files, so frames were minified and unnameable (e.g. `p @ src_17njnbr._.js:0`). Build change (Turbopack / Next 16): - Under Turbopack the only source-map lever is `turbopackSourceMaps`, whose build-time default IS `productionBrowserSourceMaps` (already true). So server chunk maps (.next/server/**/*.js.map) are already emitted at build time; `experimental.serverSourceMaps` is webpack-only and ignored by Turbopack. Reworded next.config.mjs comment to state this accurately. - output:'standalone' traces via @vercel/nft, which follows import/require/fs and does NOT copy sibling .map files, so the maps were dropped from the image. Dockerfile now stages just the server-chunk maps (structure-preserving tar) in the builder and overlays them onto .next/server in the runner. Maps are inert at runtime (loaded only by an inspector/stack resolver) -> no request-path perf cost. Cost is build time + ~order-of-tens-of-MB image size. Resolver tool: - scripts/resolve-cpuprofile.mjs maps each frame's (chunk.js, line, col) back to original {source, line, name} via the `source-map` package, ranks hottest self-time leaves, and reconstructs the longest-synchronous-block stack, named. - Verified end-to-end against a real esbuild-minified bundle + map (synthetic profile frames resolved back to original fn names + .ts locations). Prod proof awaits the next map-enabled deploy + a fresh capture. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * refactor(build): publish server maps as on-demand artifact, not in runtime image The previous approach overlaid all server .js.map files into the runtime image, adding ~761 MB to every prod pod (11,305 maps) for a debug aid that is only needed OFFLINE by the cpuprofile resolver. Instead: - Dockerfile: revert the runtime-stage map COPY (runtime image is lean again). Add a `FROM scratch AS maps` target holding ONLY the staged server maps; it shares every builder layer (cache hit) and is published separately. - resolve-cpuprofile.mjs: add `--image <tag-or-ref>` mode that fetches that build's maps from ghcr.io/civitai/civitai-web-maps:<tag> via `crane export` (falls back to `oras pull`), then resolves. Keeps the local `--maps <dir>` mode. Maps are keyed by the exact image tag so a profile from image X resolves against X's maps. The Tekton build pipeline (datapacket-talos) publishes the `maps` target to the sibling repo after the main build+push, reusing the same ghcr creds, as a non-fatal step. Verified end-to-end: pushed a synthetic maps image to a local registry, then `--image` fetched + extracted it and de-minified frames to original src/*.ts functions. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * fix(source-maps): correct stale next.config comment + clean up resolver temp dir on error Audit follow-ups on the artifact-based source-maps PR: - next.config.mjs: the comment still described the pre-revision behavior (maps baked into the runtime image). Reworded to reflect the on-demand maps artifact. - resolve-cpuprofile.mjs: wrap the post-fetch body in try/finally so the fetched maps temp dir (hundreds of MB) is always cleaned up, even if resolution throws (previously leaked on the --image error path). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-10 08:55:04 -05:00
# Server source maps (.next/server/**/*.js.map) are emitted by the build
# (productionBrowserSourceMaps -> turbopackSourceMaps) but @vercel/nft does NOT
# trace sibling .map files into .next/standalone, so they never reach runtime.
# Collect ONLY the server-chunk maps into a structure-preserving staging dir.
# These are NOT shipped in the runtime image (they added ~761 MB to every prod
# pod — too much for a debug aid). Instead they are published as a separate,
# fetched-on-demand `maps` artifact image (see the `maps` target below + the
# Tekton maps-publish step), keyed by the same tag as the runtime image, so a
# `.cpuprofile` captured from image X can be de-minified offline against X's maps.
# Build-chunk map filenames are content hashes (no spaces/newlines), so the
# newline-delimited `tar -T -` files-from list is safe and works under both GNU tar
# and busybox tar (alpine). `tar | tar` preserves the dir structure
# (e.g. chunks/<hash>.js.map) so each map keeps its .next/server-relative path.
# (Comments must stay OUTSIDE the RUN: Docker collapses the \-continuations into one
# line, where an inline `#` would swallow the rest of the command.)
RUN mkdir -p /app/server-maps && \
cd /app/.next/server && \
{ find . -name '*.js.map' | tar -cf - -T - | tar -xf - -C /app/server-maps || true; } && \
echo "Staged $(find /app/server-maps -name '*.js.map' | wc -l) server source maps ($(du -sh /app/server-maps | cut -f1))"
##### MAPS ARTIFACT (fetched on-demand; NOT part of the runtime image)
#
# A minimal `FROM scratch` image holding ONLY the staged server source maps,
# under /server-maps mirroring the .next/server tree. Published to a sibling
# registry repo (ghcr.io/civitai/civitai-web-maps:<same-tag>) by the Tekton
# maps-publish step using the SAME ghcr credentials as the runtime push — no new
# secrets. It shares every builder layer with the runtime build, so building this
# target is a buildkit cache hit plus one small layer; it is never pulled by a
# running pod. The cpuprofile resolver fetches it on demand keyed by image tag
# (scripts/resolve-cpuprofile.mjs --image ...).
FROM scratch AS maps
COPY --from=builder /app/server-maps/ /server-maps/
2023-01-24 17:10:25 -07:00
##### RUNNER
FROM node:20-alpine3.20 AS runner
2023-01-24 17:10:25 -07:00
WORKDIR /app
2025-09-04 12:20:30 -05:00
ENV NODE_ENV=production
2023-01-24 17:10:25 -07:00
# ENV NEXT_TELEMETRY_DISABLED 1
RUN addgroup --system --gid 1001 nodejs
RUN adduser --system --uid 1001 nextjs
COPY --from=builder /app/next.config.mjs ./
COPY --from=builder /app/public ./public
COPY --from=builder /app/package.json ./package.json
COPY --from=builder --chown=nextjs:nodejs /app/.next/standalone ./
COPY --from=builder --chown=nextjs:nodejs /app/.next/static ./.next/static
feat(build): ship server source maps for prod CPU-profile de-minification (#2460) * feat(build): ship server source maps for prod CPU-profile de-minification Prod pods capture V8 .cpuprofiles to find event-loop blockers, but the standalone image shipped zero server .js.map files, so frames were minified and unnameable (e.g. `p @ src_17njnbr._.js:0`). Build change (Turbopack / Next 16): - Under Turbopack the only source-map lever is `turbopackSourceMaps`, whose build-time default IS `productionBrowserSourceMaps` (already true). So server chunk maps (.next/server/**/*.js.map) are already emitted at build time; `experimental.serverSourceMaps` is webpack-only and ignored by Turbopack. Reworded next.config.mjs comment to state this accurately. - output:'standalone' traces via @vercel/nft, which follows import/require/fs and does NOT copy sibling .map files, so the maps were dropped from the image. Dockerfile now stages just the server-chunk maps (structure-preserving tar) in the builder and overlays them onto .next/server in the runner. Maps are inert at runtime (loaded only by an inspector/stack resolver) -> no request-path perf cost. Cost is build time + ~order-of-tens-of-MB image size. Resolver tool: - scripts/resolve-cpuprofile.mjs maps each frame's (chunk.js, line, col) back to original {source, line, name} via the `source-map` package, ranks hottest self-time leaves, and reconstructs the longest-synchronous-block stack, named. - Verified end-to-end against a real esbuild-minified bundle + map (synthetic profile frames resolved back to original fn names + .ts locations). Prod proof awaits the next map-enabled deploy + a fresh capture. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * refactor(build): publish server maps as on-demand artifact, not in runtime image The previous approach overlaid all server .js.map files into the runtime image, adding ~761 MB to every prod pod (11,305 maps) for a debug aid that is only needed OFFLINE by the cpuprofile resolver. Instead: - Dockerfile: revert the runtime-stage map COPY (runtime image is lean again). Add a `FROM scratch AS maps` target holding ONLY the staged server maps; it shares every builder layer (cache hit) and is published separately. - resolve-cpuprofile.mjs: add `--image <tag-or-ref>` mode that fetches that build's maps from ghcr.io/civitai/civitai-web-maps:<tag> via `crane export` (falls back to `oras pull`), then resolves. Keeps the local `--maps <dir>` mode. Maps are keyed by the exact image tag so a profile from image X resolves against X's maps. The Tekton build pipeline (datapacket-talos) publishes the `maps` target to the sibling repo after the main build+push, reusing the same ghcr creds, as a non-fatal step. Verified end-to-end: pushed a synthetic maps image to a local registry, then `--image` fetched + extracted it and de-minified frames to original src/*.ts functions. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * fix(source-maps): correct stale next.config comment + clean up resolver temp dir on error Audit follow-ups on the artifact-based source-maps PR: - next.config.mjs: the comment still described the pre-revision behavior (maps baked into the runtime image). Reworded to reflect the on-demand maps artifact. - resolve-cpuprofile.mjs: wrap the post-fetch body in try/finally so the fetched maps temp dir (hundreds of MB) is always cleaned up, even if resolution throws (previously leaked on the --image error path). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-10 08:55:04 -05:00
# NOTE: server source maps are intentionally NOT copied into the runtime image.
# They are published as the separate `maps` target above (fetched on-demand by
# the cpuprofile resolver), keeping the prod pod lean (~761 MB smaller).
2023-01-24 17:10:25 -07:00
USER nextjs
EXPOSE 3000
2025-09-04 12:20:30 -05:00
ENV PORT=3000
ENV NEXT_TELEMETRY_DISABLED=1
2023-01-24 17:10:25 -07:00
2024-05-23 14:31:49 -05:00
CMD ["node", "--", "server.js", "--", "--expose-gc"]