forked from useplunk/plunk
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathDockerfile
More file actions
359 lines (303 loc) · 16 KB
/
Dockerfile
File metadata and controls
359 lines (303 loc) · 16 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
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
# Multi-stage Dockerfile for Plunk
# Creates a single image containing all applications (API, Worker, Web, Landing, Wiki)
# Use SERVICE environment variable to specify which service to run
# ============================================
# Stage 1: Dependencies (All dependencies for building)
# ============================================
# Use build platform (AMD64) to install dependencies, avoiding QEMU issues
FROM --platform=$BUILDPLATFORM node:20-slim AS deps
ARG TARGETPLATFORM
ARG BUILDPLATFORM
WORKDIR /app
# Install curl for health checks
RUN apt-get update && apt-get install -y curl && rm -rf /var/lib/apt/lists/*
# Enable Corepack and set Yarn version
RUN corepack enable && corepack prepare yarn@4.9.1 --activate
# Copy Yarn configuration and release
COPY .yarnrc.yml ./
COPY .yarn/releases ./.yarn/releases
# Copy package files for dependency installation
COPY package.json yarn.lock ./
# Copy workspace package.json files
COPY apps/api/package.json ./apps/api/
COPY apps/smtp/package.json ./apps/smtp/
COPY apps/web/package.json ./apps/web/
COPY apps/landing/package.json ./apps/landing/
COPY apps/wiki/package.json ./apps/wiki/
COPY packages/db/package.json ./packages/db/
COPY packages/ui/package.json ./packages/ui/
COPY packages/shared/package.json ./packages/shared/
COPY packages/types/package.json ./packages/types/
COPY packages/email/package.json ./packages/email/
COPY packages/typescript-config/package.json ./packages/typescript-config/
COPY packages/eslint-config/package.json ./packages/eslint-config/
# Copy wiki source files for postinstall script (fumadocs-mdx)
COPY apps/wiki/content ./apps/wiki/content
COPY apps/wiki/lib ./apps/wiki/lib
COPY apps/wiki/source.config.ts ./apps/wiki/source.config.ts
COPY apps/wiki/mdx-components.tsx ./apps/wiki/mdx-components.tsx
COPY apps/wiki/next.config.mjs ./apps/wiki/next.config.mjs
COPY apps/wiki/tsconfig.json ./apps/wiki/tsconfig.json
# Install dependencies (runs on build platform, fetches binaries for target platform)
# Use cache mounts for Yarn cache to speed up dependency installation
RUN --mount=type=cache,target=/root/.yarn/berry/cache,sharing=locked \
--mount=type=cache,target=/root/.cache/yarn,sharing=locked \
echo "Building on $BUILDPLATFORM for $TARGETPLATFORM" && \
yarn install --immutable
# ============================================
# Stage 1b: Production Dependencies for API/SMTP
# ============================================
# Install only production dependencies needed for API and SMTP services
FROM --platform=$BUILDPLATFORM node:20-slim AS prod-deps
ARG TARGETPLATFORM
ARG BUILDPLATFORM
WORKDIR /app
# Enable Corepack and set Yarn version
RUN corepack enable && corepack prepare yarn@4.9.1 --activate
# Copy Yarn configuration
COPY .yarnrc.yml ./
COPY .yarn/releases ./.yarn/releases
# Copy all package.json files (needed for workspace resolution)
COPY package.json yarn.lock ./
COPY apps/api/package.json ./apps/api/
COPY apps/smtp/package.json ./apps/smtp/
COPY packages/db/package.json ./packages/db/
COPY packages/shared/package.json ./packages/shared/
COPY packages/types/package.json ./packages/types/
COPY packages/email/package.json ./packages/email/
# Install ONLY production dependencies for api, smtp, and their workspace dependencies
# This excludes devDependencies and unneeded workspaces (web, landing, wiki, ui)
RUN --mount=type=cache,target=/root/.yarn/berry/cache,sharing=locked \
--mount=type=cache,target=/root/.cache/yarn,sharing=locked \
echo "Installing production dependencies for API/SMTP on $BUILDPLATFORM for $TARGETPLATFORM" && \
yarn workspaces focus api smtp --production
# ============================================
# Stage 2: Builder
# ============================================
# Builder runs on target platform to generate platform-specific artifacts
FROM node:20-slim AS builder
ARG TARGETPLATFORM
# Build-time arguments for URL configuration
# These are only used during the build process (for wiki OpenAPI generation and static assets)
# Runtime URLs are configured via *_DOMAIN and USE_HTTPS environment variables at container startup
ARG API_URI=https://next-api.useplunk.com
ARG DASHBOARD_URI=https://next-app.useplunk.com
ARG LANDING_URI=https://next.useplunk.com
ARG WIKI_URI=https://next-wiki.useplunk.com
WORKDIR /app
# Install OpenSSL for Prisma
RUN apt-get update && apt-get install -y openssl && rm -rf /var/lib/apt/lists/*
# Enable Corepack and set Yarn version
RUN corepack enable && corepack prepare yarn@4.9.1 --activate
# Copy dependencies from deps stage
COPY --from=deps /app/node_modules ./node_modules
COPY --from=deps /app/.yarn ./.yarn
COPY --from=deps /app/.yarnrc.yml ./
COPY --from=deps /app/package.json ./
COPY --from=deps /app/yarn.lock ./
# ============================================
# OPTIMIZATION: Copy and build in layers for better Docker cache utilization
# Docker will only rebuild from the first changed layer, not everything
# ============================================
# Copy root config files needed for Turbo
COPY turbo.json ./
# Copy manifest generation script
COPY docker/generate-url-manifest.sh /usr/local/bin/
RUN chmod +x /usr/local/bin/generate-url-manifest.sh
# Step 1: Copy and build shared packages (these change less frequently)
# Shared packages are dependencies for apps, so build them first
COPY packages ./packages
RUN yarn workspace @plunk/db db:generate
RUN --mount=type=cache,target=/app/.turbo,sharing=locked \
API_URI=${API_URI} \
DASHBOARD_URI=${DASHBOARD_URI} \
LANDING_URI=${LANDING_URI} \
WIKI_URI=${WIKI_URI} \
NEXT_PUBLIC_API_URI=${API_URI} \
NEXT_PUBLIC_DASHBOARD_URI=${DASHBOARD_URI} \
NEXT_PUBLIC_LANDING_URI=${LANDING_URI} \
NEXT_PUBLIC_WIKI_URI=${WIKI_URI} \
yarn turbo build --filter="@plunk/*"
# Step 2: Copy and build API (backend services)
COPY apps/api ./apps/api
COPY apps/smtp ./apps/smtp
RUN --mount=type=cache,target=/app/.turbo,sharing=locked \
API_URI=${API_URI} \
DASHBOARD_URI=${DASHBOARD_URI} \
LANDING_URI=${LANDING_URI} \
WIKI_URI=${WIKI_URI} \
NEXT_PUBLIC_API_URI=${API_URI} \
NEXT_PUBLIC_DASHBOARD_URI=${DASHBOARD_URI} \
NEXT_PUBLIC_LANDING_URI=${LANDING_URI} \
NEXT_PUBLIC_WIKI_URI=${WIKI_URI} \
yarn turbo build --filter=api --filter=smtp
# Step 3: Copy and build Wiki (includes API doc generation)
COPY apps/wiki ./apps/wiki
# Generate OpenAPI spec and docs (URLs will be placeholder values for runtime replacement)
RUN cd apps/wiki && \
node scripts/generate-openapi.js && \
npx tsx scripts/generate-docs.mts && \
npx fumadocs-mdx && \
cd ../..
# Build wiki with placeholder URLs (replaced at container startup)
RUN --mount=type=cache,target=/app/.turbo,sharing=locked \
API_URI=${API_URI} \
DASHBOARD_URI=${DASHBOARD_URI} \
LANDING_URI=${LANDING_URI} \
WIKI_URI=${WIKI_URI} \
NEXT_PUBLIC_API_URI=${API_URI} \
NEXT_PUBLIC_DASHBOARD_URI=${DASHBOARD_URI} \
NEXT_PUBLIC_LANDING_URI=${LANDING_URI} \
NEXT_PUBLIC_WIKI_URI=${WIKI_URI} \
yarn turbo build --filter=wiki
# Generate sitemap for wiki
RUN NEXT_PUBLIC_WIKI_URI=${WIKI_URI} yarn workspace wiki sitemap
# Generate URL replacement manifest for wiki (build-time optimization)
RUN generate-url-manifest.sh wiki /app/apps/wiki
# Step 4: Copy and build Web dashboard
COPY apps/web ./apps/web
RUN --mount=type=cache,target=/app/.turbo,sharing=locked \
API_URI=${API_URI} \
DASHBOARD_URI=${DASHBOARD_URI} \
LANDING_URI=${LANDING_URI} \
WIKI_URI=${WIKI_URI} \
NEXT_PUBLIC_API_URI=${API_URI} \
NEXT_PUBLIC_DASHBOARD_URI=${DASHBOARD_URI} \
NEXT_PUBLIC_LANDING_URI=${LANDING_URI} \
NEXT_PUBLIC_WIKI_URI=${WIKI_URI} \
yarn turbo build --filter=web
# Generate sitemap for web
RUN NEXT_PUBLIC_DASHBOARD_URI=${DASHBOARD_URI} yarn workspace web sitemap
# Generate URL replacement manifest for web (build-time optimization)
RUN generate-url-manifest.sh web /app/apps/web
# Step 5: Copy and build Landing page
COPY apps/landing ./apps/landing
RUN --mount=type=cache,target=/app/.turbo,sharing=locked \
API_URI=${API_URI} \
DASHBOARD_URI=${DASHBOARD_URI} \
LANDING_URI=${LANDING_URI} \
WIKI_URI=${WIKI_URI} \
NEXT_PUBLIC_API_URI=${API_URI} \
NEXT_PUBLIC_DASHBOARD_URI=${DASHBOARD_URI} \
NEXT_PUBLIC_LANDING_URI=${LANDING_URI} \
NEXT_PUBLIC_WIKI_URI=${WIKI_URI} \
yarn turbo build --filter=landing
# Generate sitemap for landing
RUN NEXT_PUBLIC_LANDING_URI=${LANDING_URI} yarn workspace landing sitemap
# Generate URL replacement manifest for landing (build-time optimization)
RUN generate-url-manifest.sh landing /app/apps/landing
# Copy any remaining root files (if needed)
COPY . .
# Ensure directories exist (create empty ones if build didn't generate them)
RUN mkdir -p \
apps/web/public \
apps/landing/public \
apps/wiki/public \
apps/web/.next/standalone \
apps/landing/.next/standalone \
apps/wiki/.next/standalone
# ============================================
# Stage 3: Production Runtime
# ============================================
FROM node:20-alpine AS runner
ARG TARGETPLATFORM
ARG BUILDPLATFORM
WORKDIR /app
# Install OpenSSL for Prisma, curl for health checks, nginx, and gettext (for envsubst)
RUN apk add --no-cache openssl curl nginx gettext
# Install PM2 globally for process management
# Use cache mount and specific version to prevent hangs
RUN --mount=type=cache,target=/root/.npm \
npm install -g pm2@5.4.2 --prefer-offline --no-audit
# Create non-root user for security
RUN addgroup --system --gid 1001 nodejs
RUN adduser --system --uid 1001 plunk
# Create nginx directories and set permissions
RUN mkdir -p /var/log/nginx /var/lib/nginx /run/nginx && \
chown -R plunk:nodejs /var/log/nginx /var/lib/nginx /run/nginx /etc/nginx
# ============================================
# Copy API and SMTP services with minimal dependencies
# ============================================
# Copy built API and SMTP services
COPY --from=builder --chown=plunk:nodejs /app/apps/api/dist ./apps/api/dist
COPY --from=builder --chown=plunk:nodejs /app/apps/smtp/dist ./apps/smtp/dist
# Copy ONLY production dependencies for API/SMTP (excludes dev deps and frontend packages)
COPY --from=prod-deps --chown=plunk:nodejs /app/node_modules ./node_modules
# Copy Prisma client from builder (includes generated client with correct platform binaries)
COPY --from=builder --chown=plunk:nodejs /app/node_modules/.prisma ./node_modules/.prisma
COPY --from=builder --chown=plunk:nodejs /app/node_modules/@prisma ./node_modules/@prisma
# Copy only the shared packages that are built (not source files)
# These are needed by API/SMTP at runtime
COPY --from=builder --chown=plunk:nodejs /app/packages/db/dist ./packages/db/dist
COPY --from=builder --chown=plunk:nodejs /app/packages/db/package.json ./packages/db/package.json
COPY --from=builder --chown=plunk:nodejs /app/packages/shared/dist ./packages/shared/dist
COPY --from=builder --chown=plunk:nodejs /app/packages/shared/package.json ./packages/shared/package.json
COPY --from=builder --chown=plunk:nodejs /app/packages/email/dist ./packages/email/dist
COPY --from=builder --chown=plunk:nodejs /app/packages/email/package.json ./packages/email/package.json
COPY --from=builder --chown=plunk:nodejs /app/packages/types/dist ./packages/types/dist
COPY --from=builder --chown=plunk:nodejs /app/packages/types/package.json ./packages/types/package.json
# Copy Prisma schema (needed for migrations at runtime)
COPY --from=builder --chown=plunk:nodejs /app/packages/db/prisma ./packages/db/prisma
# Copy root package.json and workspace config (needed for yarn workspace commands in entrypoint)
COPY --from=builder --chown=plunk:nodejs /app/package.json ./
COPY --from=prod-deps --chown=plunk:nodejs /app/.yarnrc.yml ./
COPY --from=prod-deps --chown=plunk:nodejs /app/.yarn ./.yarn
COPY --from=prod-deps --chown=plunk:nodejs /app/yarn.lock ./
# Copy API/SMTP package.json files
COPY --from=builder --chown=plunk:nodejs /app/apps/api/package.json ./apps/api/
COPY --from=builder --chown=plunk:nodejs /app/apps/smtp/package.json ./apps/smtp/
# ============================================
# Copy Next.js apps with their standalone builds
# ============================================
# Next.js standalone mode bundles all dependencies internally, so we don't need
# to copy node_modules for these apps - they're completely self-contained
# Web app - standalone build with static assets
COPY --from=builder --chown=plunk:nodejs /app/apps/web/.next/standalone ./apps/web/.next/standalone
COPY --from=builder --chown=plunk:nodejs /app/apps/web/public ./apps/web/.next/standalone/apps/web/public
COPY --from=builder --chown=plunk:nodejs /app/apps/web/.next/static ./apps/web/.next/standalone/apps/web/.next/static
# Copy URL replacement manifests to standalone directory
COPY --from=builder --chown=plunk:nodejs /app/apps/web/.next/url-manifest.txt ./apps/web/.next/standalone/apps/web/.next/url-manifest.txt
COPY --from=builder --chown=plunk:nodejs /app/apps/web/.next/sitemap-manifest.txt ./apps/web/.next/standalone/apps/web/.next/sitemap-manifest.txt
# Landing app - standalone build with static assets
COPY --from=builder --chown=plunk:nodejs /app/apps/landing/.next/standalone ./apps/landing/.next/standalone
COPY --from=builder --chown=plunk:nodejs /app/apps/landing/public ./apps/landing/.next/standalone/apps/landing/public
COPY --from=builder --chown=plunk:nodejs /app/apps/landing/.next/static ./apps/landing/.next/standalone/apps/landing/.next/static
# Copy URL replacement manifests to standalone directory
COPY --from=builder --chown=plunk:nodejs /app/apps/landing/.next/url-manifest.txt ./apps/landing/.next/standalone/apps/landing/.next/url-manifest.txt
COPY --from=builder --chown=plunk:nodejs /app/apps/landing/.next/sitemap-manifest.txt ./apps/landing/.next/standalone/apps/landing/.next/sitemap-manifest.txt
# Wiki app - standalone build with static assets and OpenAPI spec
COPY --from=builder --chown=plunk:nodejs /app/apps/wiki/.next/standalone ./apps/wiki/.next/standalone
COPY --from=builder --chown=plunk:nodejs /app/apps/wiki/public ./apps/wiki/.next/standalone/apps/wiki/public
COPY --from=builder --chown=plunk:nodejs /app/apps/wiki/.next/static ./apps/wiki/.next/standalone/apps/wiki/.next/static
COPY --from=builder --chown=plunk:nodejs /app/apps/wiki/openapi.local.json ./apps/wiki/.next/standalone/apps/wiki/openapi.local.json
# Copy URL replacement manifests to standalone directory
COPY --from=builder --chown=plunk:nodejs /app/apps/wiki/.next/url-manifest.txt ./apps/wiki/.next/standalone/apps/wiki/.next/url-manifest.txt
COPY --from=builder --chown=plunk:nodejs /app/apps/wiki/.next/sitemap-manifest.txt ./apps/wiki/.next/standalone/apps/wiki/.next/sitemap-manifest.txt
# Copy full .next directories for the entrypoint script (URL replacement via find command)
# These are much smaller than node_modules and needed for runtime URL replacement
COPY --from=builder --chown=plunk:nodejs /app/apps/web/.next ./apps/web/.next
COPY --from=builder --chown=plunk:nodejs /app/apps/landing/.next ./apps/landing/.next
COPY --from=builder --chown=plunk:nodejs /app/apps/wiki/.next ./apps/wiki/.next
COPY --from=builder --chown=plunk:nodejs /app/apps/wiki/openapi.local.json ./apps/wiki/openapi.local.json
# ============================================
# Copy runtime configuration
# ============================================
# Copy nginx configuration templates and setup script
COPY --chown=plunk:nodejs docker/nginx/ /app/docker/nginx/
RUN chmod +x /app/docker/nginx/setup-nginx.sh
# Copy optimized URL replacement script
COPY --chown=plunk:nodejs docker/replace-urls-optimized.sh /app/docker/
RUN chmod +x /app/docker/replace-urls-optimized.sh
# Copy entrypoint script
COPY --chown=plunk:nodejs docker-entrypoint-nginx.sh /usr/local/bin/
RUN chmod +x /usr/local/bin/docker-entrypoint-nginx.sh
USER plunk
# Expose nginx port (default 80), SMTP ports (465, 587)
# Port 80 is also used for ACME HTTP-01 challenges
EXPOSE 80 465 587
# Health check through nginx
HEALTHCHECK --interval=30s --timeout=10s --start-period=60s --retries=3 \
CMD curl -f http://localhost:80/ || exit 1
# Default to running all services via entrypoint
ENV SERVICE=all
ENTRYPOINT ["docker-entrypoint-nginx.sh"]