-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathDockerfile
More file actions
117 lines (104 loc) · 4.88 KB
/
Copy pathDockerfile
File metadata and controls
117 lines (104 loc) · 4.88 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
# syntax=docker/dockerfile:1.7
# --- Multi-stage Next.js production build ---------------------------------
# deps: cacheable install layer
# build: generate Prisma client + next build (standalone output)
# runner: minimal runtime image; runs migrations on startup, then next start
FROM node:20-alpine AS base
RUN corepack enable pnpm
WORKDIR /app
# ---- deps -----------------------------------------------------------------
FROM base AS deps
COPY package.json pnpm-lock.yaml* ./
# pnpm-lock.yaml may not exist on first build; fall back to install.
RUN --mount=type=cache,id=pnpm,target=/root/.local/share/pnpm/store \
if [ -f pnpm-lock.yaml ]; then pnpm install --frozen-lockfile; \
else pnpm install --no-frozen-lockfile; fi
# ---- docs -----------------------------------------------------------------
# VitePress docs are staged into public/docs for the Next app. Keep their
# dependency/build cache isolated from app source changes so a normal app-only
# deploy does not rebuild docs.
FROM base AS docs-deps
COPY package.json pnpm-lock.yaml* ./
COPY docs/package.json docs/pnpm-lock.yaml* ./docs/
RUN --mount=type=cache,id=pnpm-docs,target=/root/.local/share/pnpm/store \
if [ -f docs/pnpm-lock.yaml ]; then pnpm --dir docs --ignore-workspace install --frozen-lockfile; \
else pnpm --dir docs --ignore-workspace install --no-frozen-lockfile; fi
FROM docs-deps AS docs-build
WORKDIR /app
COPY docs ./docs
RUN pnpm --dir docs --ignore-workspace build
# ---- prisma client --------------------------------------------------------
# Shared generated Prisma client for both app build and worker runtime. Keeping
# this before the Next build lets the worker image build without compiling the
# web app.
FROM base AS prisma-client
COPY --from=deps /app/node_modules ./node_modules
COPY package.json pnpm-lock.yaml* ./
COPY prisma ./prisma
RUN pnpm prisma generate
# ---- build ----------------------------------------------------------------
FROM base AS build
COPY --from=prisma-client /app/node_modules ./node_modules
COPY . .
COPY --from=docs-build /app/docs/.vitepress/dist ./docs/.vitepress/dist
ENV NEXT_TELEMETRY_DISABLED=1
RUN STAGE_ONLY=1 pnpm build
# ---- worker ---------------------------------------------------------------
# BullMQ maintenance/webhook workers are a separate process from the Next.js
# server. Keep a dedicated target with source + generated Prisma client, but
# do not copy the full Next build output into the worker image.
FROM base AS worker
WORKDIR /app
ENV NODE_ENV=production
ENV NEXT_TELEMETRY_DISABLED=1
# Build identity — same stamp as the runner stage so the worker can report
# which commit it's running (and so a blank stamp isn't mistaken for a stale
# build). Passed at `docker compose build`; blank in a bare `docker build`.
ARG GIT_SHA=""
ARG BUILD_TIME=""
ENV FORGE_GIT_SHA=$GIT_SHA
ENV FORGE_BUILD_TIME=$BUILD_TIME
COPY --from=prisma-client /app/node_modules ./node_modules
# `next-env.d.ts` is generated by Next and intentionally gitignored. The worker
# runs through tsx and does not need it; omitting it keeps clean-clone builds
# reproducible instead of relying on residue from a developer checkout.
COPY package.json pnpm-lock.yaml* tsconfig.json next.config.ts ./
COPY prisma ./prisma
COPY scripts ./scripts
COPY plugins ./plugins
COPY src ./src
CMD ["pnpm", "worker"]
# ---- runner ---------------------------------------------------------------
FROM node:20-alpine AS runner
RUN apk add --no-cache libc6-compat tini
WORKDIR /app
ENV NODE_ENV=production
ENV NEXT_TELEMETRY_DISABLED=1
ENV PORT=3000
ENV HOSTNAME=0.0.0.0
# Build identity — passed at `docker compose build` (deploy ritual exports
# GIT_SHA + BUILD_TIME). Baked into the image so `system.buildInfo` can report
# exactly which commit is running. Blank in a bare `docker build`.
ARG GIT_SHA=""
ARG BUILD_TIME=""
ENV FORGE_GIT_SHA=$GIT_SHA
ENV FORGE_BUILD_TIME=$BUILD_TIME
# Install Prisma CLI at a pinned version for the boot-time migrate step.
# The client + query engine travel with the standalone bundle via
# outputFileTracingIncludes; only the CLI binary is extra here.
RUN npm install -g --no-fund --no-audit prisma@6.19.3
# Standalone output + static assets + public + schema.
COPY --from=build /app/.next/standalone ./
COPY --from=build /app/.next/static ./.next/static
COPY --from=build /app/public ./public
COPY --from=build /app/prisma ./prisma
# Flatten the Prisma client: @prisma/client's default.js does
# `require(".prisma/client/default")`. With pnpm, `.prisma` lives inside
# the virtual store, which breaks the standalone bundle. Drop it at the
# top-level `node_modules/.prisma` path the client resolver expects.
COPY --from=build /app/node_modules/.pnpm/@prisma+client*/node_modules/.prisma ./node_modules/.prisma
# On boot: apply migrations, then start Next.
COPY docker/entrypoint.sh /usr/local/bin/entrypoint.sh
RUN chmod +x /usr/local/bin/entrypoint.sh
EXPOSE 3000
ENTRYPOINT ["/sbin/tini", "--", "/usr/local/bin/entrypoint.sh"]