@plumbus/core 0.2.3 → 0.2.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/instructions/deployment.md +256 -112
- package/package.json +1 -1
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# Deployment
|
|
2
2
|
|
|
3
|
-
Plumbus applications deploy as up to three services — a **backend** (Fastify API server), a **frontend** (Next.js), and optionally an **admin dashboard** (Next.js) — backed by PostgreSQL and Redis.
|
|
3
|
+
Plumbus applications deploy as up to three services — a **backend** (Fastify API server), a **frontend** (Next.js), and optionally an **admin dashboard** (Next.js) — backed by PostgreSQL and Redis.
|
|
4
4
|
|
|
5
5
|
## Service Topology
|
|
6
6
|
|
|
@@ -19,46 +19,116 @@ Plumbus applications deploy as up to three services — a **backend** (Fastify A
|
|
|
19
19
|
+-------+ +-------+
|
|
20
20
|
```
|
|
21
21
|
|
|
22
|
-
- **Frontend** and **admin** proxy API calls to the backend
|
|
22
|
+
- **Frontend** and **admin** proxy API calls to the backend server-side. Browsers never connect directly to the backend.
|
|
23
23
|
- **Backend** connects to PostgreSQL (data) and Redis (job queue, event dispatch).
|
|
24
24
|
|
|
25
|
-
|
|
25
|
+
---
|
|
26
26
|
|
|
27
|
-
|
|
27
|
+
## Docker Rules — MUST follow all of these
|
|
28
28
|
|
|
29
|
-
|
|
29
|
+
These rules are critical. Violating any one causes build failures or bloated images.
|
|
30
30
|
|
|
31
|
-
|
|
31
|
+
### Rule 1: Generate `.plumbus/` inside Docker
|
|
32
32
|
|
|
33
|
-
|
|
33
|
+
- **DO**: Run `plumbus generate` in a Docker build stage.
|
|
34
|
+
- **DO NOT**: Copy `.plumbus/` from the host. It may not exist in CI/fresh clones.
|
|
34
35
|
|
|
35
|
-
|
|
36
|
+
### Rule 2: Always copy `.npmrc` into Docker images
|
|
36
37
|
|
|
37
|
-
|
|
38
|
+
- **DO**: `COPY .npmrc ./` before `pnpm install`.
|
|
39
|
+
- **DO NOT**: Skip `.npmrc`. It has `public-hoist-pattern` entries required for `next build` to find `next`, `react`, `tailwindcss`.
|
|
40
|
+
- **Symptom if violated**: `Cannot find module 'next'` during build.
|
|
38
41
|
|
|
39
|
-
|
|
42
|
+
### Rule 3: Never copy `pnpm-workspace.yaml` with local dev overrides
|
|
40
43
|
|
|
41
|
-
|
|
44
|
+
- **DO**: Copy the clean `pnpm-workspace.yaml` (lists only `frontend` and `frontend-admin`).
|
|
45
|
+
- **DO NOT**: Copy a workspace yaml that contains `link:../Plumbus/...` dev overrides.
|
|
46
|
+
- **TIP**: If your project uses `dev:link`/`dev:unlink` scripts to swap workspace configs, make sure the clean version is active before building Docker images.
|
|
42
47
|
|
|
43
|
-
|
|
48
|
+
### Rule 4: Use `FROM deps AS build` (preserve pnpm symlinks in build stage)
|
|
44
49
|
|
|
45
|
-
|
|
50
|
+
- **DO**: `FROM deps AS build` — inherits the filesystem with intact symlinks.
|
|
51
|
+
- **DO NOT**: `FROM base AS build` with `COPY --from=deps /app/node_modules ./node_modules`. Docker COPY breaks pnpm symlinks.
|
|
46
52
|
|
|
47
|
-
|
|
53
|
+
### Rule 5: Use `pnpm deploy` to isolate frontend dependencies
|
|
48
54
|
|
|
49
|
-
|
|
55
|
+
- **DO**: Run `pnpm --filter {./frontend} deploy --legacy /app/deployed` to create a self-contained directory with flat `node_modules` containing ONLY the frontend's dependencies.
|
|
56
|
+
- **DO NOT**: Build Next.js from the workspace root with all monorepo dependencies visible. pnpm's symlinked store breaks Next.js file tracing, and hoisted layout includes every workspace package.
|
|
57
|
+
- **Why**: `pnpm deploy` creates isolated flat `node_modules` with just the target package's deps. Next.js standalone traces only what the frontend actually needs (~30-40 MB vs ~600+ MB).
|
|
50
58
|
|
|
51
|
-
|
|
59
|
+
### Rule 6: Never copy full `node_modules` into frontend runner stages
|
|
52
60
|
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
61
|
+
- **DO**: Copy ONLY `.next/standalone` and `.next/static` into the runner.
|
|
62
|
+
- **DO NOT**: `COPY --from=deps /app/node_modules ./node_modules` in the runner stage.
|
|
63
|
+
- **Why**: Standalone output already includes all traced modules (~60-80 MB). Copying full `node_modules` adds ~700 MB for no reason.
|
|
64
|
+
- **Target image size**: Frontend/admin images should be ~150-200 MB total, not 800 MB+.
|
|
65
|
+
|
|
66
|
+
### Rule 7: Strip frontend-only packages from backend prod deps
|
|
67
|
+
|
|
68
|
+
- **DO**: Create a separate `proddeps` stage that removes `@plumbus/ui` from `package.json` before `npm install --omit=dev`.
|
|
69
|
+
- **DO NOT**: Copy the full `node_modules` (including `@plumbus/ui`) into the backend runner.
|
|
70
|
+
- **Why**: `@plumbus/ui` pulls in `next`, `react`, `react-dom`, `tailwindcss`, `lucide-react` (~300 MB). The backend only needs `@plumbus/core` at runtime.
|
|
71
|
+
- **Target image size**: Backend should be ~500-700 MB, not 1.5 GB+.
|
|
72
|
+
|
|
73
|
+
### Rule 8: Use `ENV PATH` instead of `npx` for frontend builds
|
|
74
|
+
|
|
75
|
+
- **DO**: `ENV PATH="/app/deployed/node_modules/.bin:$PATH"` then `RUN next build`.
|
|
76
|
+
- **DO NOT**: `RUN npx next build`.
|
|
77
|
+
- **Why**: With `pnpm deploy`, binaries are in the deployed directory's `node_modules/.bin/`. Adding it to PATH ensures `next` resolves correctly.
|
|
78
|
+
|
|
79
|
+
### Rule 9: Never run migrations in the entrypoint
|
|
80
|
+
|
|
81
|
+
- **DO**: Run `npx plumbus migrate apply` as a separate CI/CD step or one-off job.
|
|
82
|
+
- **DO NOT**: Put migration commands in `entrypoint.sh` or Docker init containers.
|
|
83
|
+
- **Why**: In multi-replica deployments, concurrent migrations corrupt data.
|
|
84
|
+
|
|
85
|
+
### Rule 10: Use dynamic environment detection in `app.config.ts`
|
|
86
|
+
|
|
87
|
+
- **DO**: `environment: (process.env.NODE_ENV === 'production' ? 'production' : 'development')`
|
|
88
|
+
- **DO NOT**: Hardcode `environment: 'development'`. This causes `plumbus start` to skip production safeguards in Docker.
|
|
89
|
+
|
|
90
|
+
### Rule 11: Regenerate client types before building frontends
|
|
91
|
+
|
|
92
|
+
- **DO**: Run `plumbus ui generate` before `next build` if capability schemas changed.
|
|
93
|
+
- **DO NOT**: Build frontends with stale `frontend/lib/client.ts`.
|
|
94
|
+
|
|
95
|
+
### Rule 12: Set `output: "standalone"` in all `next.config.mjs` files
|
|
96
|
+
|
|
97
|
+
- **DO**: Add `output: "standalone"` to `next.config.mjs` for every Next.js app (frontend, admin).
|
|
98
|
+
- **DO NOT**: Deploy without standalone output. The app would need full `node_modules` at runtime.
|
|
99
|
+
|
|
100
|
+
### Rule 13: Add `outputFileTracingExcludes` in `next.config.mjs`
|
|
101
|
+
|
|
102
|
+
- **DO**: Exclude build/dev tools that are transitive deps of `@plumbus/ui` but not needed at runtime:
|
|
103
|
+
```js
|
|
104
|
+
outputFileTracingExcludes: {
|
|
105
|
+
"**": [
|
|
106
|
+
"./node_modules/typescript/**",
|
|
107
|
+
"./node_modules/@swc/core*/**",
|
|
108
|
+
],
|
|
109
|
+
},
|
|
110
|
+
```
|
|
111
|
+
- **Why**: Even with `pnpm deploy`, some transitive deps (typescript, @swc/core) are present via `@plumbus/ui` but unnecessary at runtime. Excluding them saves ~50 MB.
|
|
112
|
+
|
|
113
|
+
### Rule 14: Clean up test/build tools from backend `node_modules`
|
|
114
|
+
|
|
115
|
+
- **DO**: After `npm install --omit=dev` in the backend `proddeps` stage, delete transitive deps only needed for testing/building:
|
|
116
|
+
```dockerfile
|
|
117
|
+
RUN npm install --omit=dev && \
|
|
118
|
+
rm -rf node_modules/@vitest node_modules/vitest \
|
|
119
|
+
node_modules/playwright-core node_modules/playwright \
|
|
120
|
+
node_modules/@esbuild node_modules/esbuild \
|
|
121
|
+
node_modules/typescript
|
|
122
|
+
```
|
|
123
|
+
- **Why**: `@plumbus/core` includes vitest, playwright, esbuild, and typescript as dependencies. The backend doesn't need them at runtime.
|
|
124
|
+
|
|
125
|
+
---
|
|
56
126
|
|
|
57
127
|
## Backend Deployment
|
|
58
128
|
|
|
59
129
|
### Production Command
|
|
60
130
|
|
|
61
|
-
Use `plumbus start` for production
|
|
131
|
+
Use `plumbus start` for production:
|
|
62
132
|
|
|
63
133
|
| Behavior | `plumbus dev` | `plumbus start` |
|
|
64
134
|
|----------|---------------|------------------|
|
|
@@ -75,7 +145,7 @@ npx plumbus start --port 3000
|
|
|
75
145
|
|
|
76
146
|
### Runtime File Requirements
|
|
77
147
|
|
|
78
|
-
|
|
148
|
+
These directories **must be present** in the production image:
|
|
79
149
|
|
|
80
150
|
| Directory | Purpose |
|
|
81
151
|
|-----------|---------|
|
|
@@ -88,18 +158,20 @@ The backend uses dynamic resource discovery at startup — it scans the source t
|
|
|
88
158
|
|
|
89
159
|
### Entrypoint Pattern
|
|
90
160
|
|
|
91
|
-
**CRITICAL**: Do **not** run migrations automatically in the container entrypoint. In multi-replica deployments (Kubernetes, ECS), concurrent migration runs can corrupt data or cause extended downtime. Run migrations as a separate, deliberate step before deploying new containers.
|
|
92
|
-
|
|
93
161
|
```sh
|
|
94
|
-
#
|
|
95
|
-
npx plumbus migrate apply
|
|
96
|
-
|
|
97
|
-
# Then deploy. Container entrypoint only starts the server:
|
|
162
|
+
# entrypoint.sh — only starts the server. No migrations here.
|
|
98
163
|
#!/bin/sh
|
|
99
164
|
set -e
|
|
100
165
|
exec npx plumbus start --port 3000
|
|
101
166
|
```
|
|
102
167
|
|
|
168
|
+
Migrations run separately:
|
|
169
|
+
|
|
170
|
+
```sh
|
|
171
|
+
# Run BEFORE deploying new containers (CI/CD step or one-off job):
|
|
172
|
+
npx plumbus migrate apply
|
|
173
|
+
```
|
|
174
|
+
|
|
103
175
|
### Health Endpoints
|
|
104
176
|
|
|
105
177
|
| Endpoint | Purpose | Healthy response |
|
|
@@ -109,11 +181,13 @@ exec npx plumbus start --port 3000
|
|
|
109
181
|
|
|
110
182
|
Use `/ready` for container health checks and load balancer probes.
|
|
111
183
|
|
|
184
|
+
---
|
|
185
|
+
|
|
112
186
|
## Frontend Deployment
|
|
113
187
|
|
|
114
188
|
### Standalone Output
|
|
115
189
|
|
|
116
|
-
|
|
190
|
+
Add to `next.config.mjs`:
|
|
117
191
|
|
|
118
192
|
```js
|
|
119
193
|
const nextConfig = {
|
|
@@ -129,18 +203,12 @@ const nextConfig = {
|
|
|
129
203
|
npx next build
|
|
130
204
|
|
|
131
205
|
# Run standalone server
|
|
132
|
-
node .next/standalone/<app-dir>/server.js
|
|
133
|
-
```
|
|
134
|
-
|
|
135
|
-
Set `PORT` and `HOSTNAME` environment variables to control the listening address:
|
|
136
|
-
|
|
137
|
-
```bash
|
|
138
206
|
PORT=3001 HOSTNAME=0.0.0.0 node .next/standalone/frontend/server.js
|
|
139
207
|
```
|
|
140
208
|
|
|
141
209
|
### Static Assets
|
|
142
210
|
|
|
143
|
-
After a standalone build, static files
|
|
211
|
+
After a standalone build, copy static files alongside the server:
|
|
144
212
|
|
|
145
213
|
```
|
|
146
214
|
.next/standalone/ # Server code
|
|
@@ -149,9 +217,11 @@ After a standalone build, static files live in `.next/static/`. Copy them to the
|
|
|
149
217
|
|
|
150
218
|
### Environment Variables
|
|
151
219
|
|
|
152
|
-
- `NEXT_PUBLIC_*`
|
|
153
|
-
- `API_BASE_URL`
|
|
154
|
-
- `AUTH_SECRET`
|
|
220
|
+
- `NEXT_PUBLIC_*` — inlined at **build time**. Set before `next build`, not at runtime.
|
|
221
|
+
- `API_BASE_URL` — set at **runtime**. Internal Docker network address (e.g., `http://backend:3000`).
|
|
222
|
+
- `AUTH_SECRET` — required for cookie signing.
|
|
223
|
+
|
|
224
|
+
---
|
|
155
225
|
|
|
156
226
|
## Environment Variables Reference
|
|
157
227
|
|
|
@@ -183,26 +253,61 @@ After a standalone build, static files live in `.next/static/`. Copy them to the
|
|
|
183
253
|
| `AUTH_SECRET` | Runtime | Cookie signing secret |
|
|
184
254
|
| `AUTH_COOKIE_SECURE` | Runtime | Set to `"true"` for TLS, `"false"` for HTTP-only deployments |
|
|
185
255
|
|
|
256
|
+
---
|
|
257
|
+
|
|
186
258
|
## Docker
|
|
187
259
|
|
|
188
|
-
###
|
|
260
|
+
### Expected Image Sizes
|
|
261
|
+
|
|
262
|
+
| Image | Target Size | Too Large If |
|
|
263
|
+
|-------|-------------|--------------|
|
|
264
|
+
| Frontend | ~150-200 MB | > 400 MB |
|
|
265
|
+
| Admin | ~150-200 MB | > 400 MB |
|
|
266
|
+
| Backend | ~500-700 MB | > 1 GB |
|
|
267
|
+
|
|
268
|
+
If images exceed these sizes, you are likely not using `pnpm deploy` (Rule 5) or not stripping backend deps (Rule 7, Rule 14).
|
|
269
|
+
|
|
270
|
+
### Backend Dockerfile
|
|
189
271
|
|
|
190
272
|
```dockerfile
|
|
273
|
+
# ==============================================================
|
|
274
|
+
# BACKEND DOCKERFILE — Reference template
|
|
275
|
+
# See "Docker Rules" section above for the rationale behind each step.
|
|
276
|
+
# ==============================================================
|
|
277
|
+
|
|
191
278
|
# ---- Base ----
|
|
192
279
|
FROM node:22-alpine AS base
|
|
193
280
|
RUN corepack enable && corepack prepare pnpm@latest --activate
|
|
194
281
|
WORKDIR /app
|
|
195
282
|
|
|
196
|
-
# ----
|
|
283
|
+
# ---- Full dependencies (needed for plumbus generate and pnpm build) ----
|
|
197
284
|
FROM base AS deps
|
|
198
|
-
#
|
|
199
|
-
# (link:../Plumbus/...) that don't exist in Docker. Packages resolve from npm.
|
|
285
|
+
# DO NOT copy pnpm-workspace.yaml — it may have local dev overrides.
|
|
200
286
|
COPY package.json pnpm-lock.yaml ./
|
|
201
287
|
RUN pnpm install
|
|
202
288
|
|
|
289
|
+
# ---- Production dependencies only ----
|
|
290
|
+
# Strip @plumbus/ui and devDependencies — backend doesn't need them at runtime.
|
|
291
|
+
# This saves ~300 MB (next, react, tailwindcss, etc. are excluded).
|
|
292
|
+
# After install, remove test/build tools from @plumbus/core transitive deps.
|
|
293
|
+
FROM node:22-alpine AS proddeps
|
|
294
|
+
WORKDIR /app
|
|
295
|
+
COPY package.json ./
|
|
296
|
+
ENV PLAYWRIGHT_SKIP_BROWSER_DOWNLOAD=1
|
|
297
|
+
RUN node -e "\
|
|
298
|
+
const p = JSON.parse(require('fs').readFileSync('package.json','utf8'));\
|
|
299
|
+
for (const k of ['@plumbus/ui'])\
|
|
300
|
+
delete (p.dependencies||{})[k];\
|
|
301
|
+
delete p.devDependencies;\
|
|
302
|
+
require('fs').writeFileSync('package.json', JSON.stringify(p, null, 2));" && \
|
|
303
|
+
npm install --omit=dev && \
|
|
304
|
+
rm -rf node_modules/@vitest node_modules/vitest \
|
|
305
|
+
node_modules/playwright-core node_modules/playwright \
|
|
306
|
+
node_modules/@esbuild node_modules/esbuild \
|
|
307
|
+
node_modules/typescript
|
|
308
|
+
|
|
203
309
|
# ---- Generate .plumbus/ ----
|
|
204
|
-
#
|
|
205
|
-
# inside Docker so it always exists and matches the current app/ definitions.
|
|
310
|
+
# DO: Generate inside Docker. DO NOT: Copy from host.
|
|
206
311
|
FROM deps AS generate
|
|
207
312
|
COPY app/ ./app/
|
|
208
313
|
COPY config/ ./config/
|
|
@@ -210,6 +315,7 @@ COPY tsconfig.json ./
|
|
|
210
315
|
RUN npx plumbus generate
|
|
211
316
|
|
|
212
317
|
# ---- Build ----
|
|
318
|
+
# Uses "FROM deps" to preserve pnpm symlinks (Rule 4).
|
|
213
319
|
FROM deps AS build
|
|
214
320
|
COPY --from=generate /app/.plumbus/ ./.plumbus/
|
|
215
321
|
COPY package.json tsconfig.json ./
|
|
@@ -220,65 +326,72 @@ RUN pnpm build
|
|
|
220
326
|
|
|
221
327
|
# ---- Runner ----
|
|
222
328
|
FROM node:22-alpine AS runner
|
|
223
|
-
RUN corepack enable && corepack prepare pnpm@latest --activate
|
|
224
329
|
WORKDIR /app
|
|
225
330
|
ENV NODE_ENV=production
|
|
226
331
|
RUN addgroup --system --gid 1001 plumbus && \
|
|
227
332
|
adduser --system --uid 1001 plumbus
|
|
228
|
-
|
|
333
|
+
# Production-only node_modules from proddeps (no @plumbus/ui, no devDeps).
|
|
334
|
+
COPY --from=proddeps /app/node_modules ./node_modules
|
|
229
335
|
COPY --from=build /app/dist ./dist
|
|
230
336
|
COPY --from=generate /app/.plumbus/ ./.plumbus/
|
|
231
337
|
COPY package.json ./
|
|
232
338
|
COPY app/ ./app/
|
|
233
339
|
COPY config/ ./config/
|
|
234
340
|
COPY drizzle/ ./drizzle/
|
|
235
|
-
# Entrypoint starts the production server
|
|
236
341
|
USER plumbus
|
|
237
342
|
EXPOSE 3000
|
|
238
|
-
HEALTHCHECK CMD
|
|
343
|
+
HEALTHCHECK CMD wget -q -O /dev/null http://localhost:3000/ready || exit 1
|
|
239
344
|
ENTRYPOINT ["./entrypoint.sh"]
|
|
240
345
|
```
|
|
241
346
|
|
|
242
|
-
### Frontend Dockerfile
|
|
347
|
+
### Frontend Dockerfile
|
|
348
|
+
|
|
349
|
+
For the **admin dashboard**, replace `{./frontend}` with `{./frontend-admin}`, change port 3001 to 3002, and adjust the package filter accordingly.
|
|
243
350
|
|
|
244
351
|
```dockerfile
|
|
352
|
+
# ==============================================================
|
|
353
|
+
# FRONTEND DOCKERFILE — Reference template
|
|
354
|
+
# Uses pnpm deploy to isolate frontend deps from the monorepo.
|
|
355
|
+
# See "Docker Rules" section above for the rationale behind each step.
|
|
356
|
+
# ==============================================================
|
|
357
|
+
|
|
245
358
|
FROM node:22-alpine AS base
|
|
246
359
|
RUN corepack enable && corepack prepare pnpm@latest --activate
|
|
247
360
|
WORKDIR /app
|
|
248
361
|
|
|
362
|
+
# ---- Install full workspace (needed for pnpm deploy to resolve deps) ----
|
|
249
363
|
FROM base AS deps
|
|
250
|
-
COPY package.json pnpm-lock.yaml ./
|
|
364
|
+
COPY .npmrc package.json pnpm-lock.yaml pnpm-workspace.yaml ./
|
|
251
365
|
COPY frontend/package.json ./frontend/
|
|
252
|
-
|
|
253
|
-
# next, react, tailwindcss visible to Turbopack. Without it, next build fails.
|
|
254
|
-
COPY .npmrc ./
|
|
255
|
-
# Generate a clean workspace config — don't copy the host's pnpm-workspace.yaml
|
|
256
|
-
RUN printf 'packages:\n - "frontend"\n' > pnpm-workspace.yaml
|
|
366
|
+
COPY frontend-admin/package.json ./frontend-admin/
|
|
257
367
|
RUN pnpm install
|
|
258
368
|
|
|
259
|
-
#
|
|
260
|
-
#
|
|
369
|
+
# ---- Deploy + Build ----
|
|
370
|
+
# pnpm deploy creates a self-contained directory with flat node_modules
|
|
371
|
+
# containing ONLY the frontend's dependencies — no backend/root workspace deps.
|
|
261
372
|
FROM deps AS build
|
|
262
373
|
COPY frontend/ ./frontend/
|
|
263
|
-
|
|
264
|
-
|
|
374
|
+
RUN pnpm --filter {./frontend} deploy --legacy /app/deployed
|
|
375
|
+
ENV PATH="/app/deployed/node_modules/.bin:$PATH"
|
|
376
|
+
WORKDIR /app/deployed
|
|
377
|
+
RUN next build
|
|
265
378
|
|
|
379
|
+
# ---- Runner ----
|
|
380
|
+
# Standalone output only — includes server.js + traced node_modules (~30-40 MB).
|
|
381
|
+
# DO NOT copy node_modules from deps — standalone has everything it needs.
|
|
266
382
|
FROM node:22-alpine AS runner
|
|
267
383
|
WORKDIR /app
|
|
268
384
|
ENV NODE_ENV=production
|
|
269
385
|
RUN addgroup --system --gid 1001 nextjs && \
|
|
270
386
|
adduser --system --uid 1001 nextjs
|
|
271
|
-
COPY --from=build /app/
|
|
272
|
-
COPY --from=build /app/
|
|
387
|
+
COPY --from=build /app/deployed/.next/standalone ./
|
|
388
|
+
COPY --from=build /app/deployed/.next/static ./deployed/.next/static
|
|
273
389
|
USER nextjs
|
|
274
390
|
EXPOSE 3001
|
|
275
|
-
CMD ["node", "
|
|
391
|
+
CMD ["node", "deployed/server.js"]
|
|
276
392
|
```
|
|
277
393
|
|
|
278
|
-
|
|
279
|
-
> pnpm stores packages in a content-addressable store and creates symlinks inside `node_modules/`. Docker's `COPY --from` creates new files without preserving symlinks. By starting the build stage from the deps layer (`FROM deps`), the existing filesystem (with intact symlinks) is inherited, not copied.
|
|
280
|
-
|
|
281
|
-
### Docker Compose Topology
|
|
394
|
+
### Docker Compose
|
|
282
395
|
|
|
283
396
|
```yaml
|
|
284
397
|
services:
|
|
@@ -299,28 +412,47 @@ services:
|
|
|
299
412
|
depends_on: [backend]
|
|
300
413
|
```
|
|
301
414
|
|
|
302
|
-
###
|
|
415
|
+
### .dockerignore
|
|
303
416
|
|
|
304
|
-
|
|
305
|
-
- **Always copy `.npmrc`** into Docker images (both backend and frontend). It defines `public-hoist-pattern` entries that pnpm needs to make framework-provided packages (`next`, `react`, `tailwindcss`, etc.) visible to the application. Without it, `next build` will fail with module resolution errors.
|
|
306
|
-
- **Generate `.plumbus/` inside Docker** — never rely on it existing on the host. The backend Dockerfile should include a `generate` stage that runs `plumbus generate`. This ensures the manifests always match the current app definitions, even in CI/CD pipelines and fresh clones.
|
|
307
|
-
- **Preserve pnpm symlinks** — use `FROM deps AS build` in frontend Dockerfiles. Do **not** use `FROM base AS build` with `COPY --from=deps /app/node_modules`. Docker's COPY command does not preserve symlinks, which breaks pnpm's package resolution and causes Turbopack/Next.js to fail.
|
|
308
|
-
- **Regenerate client types** — run `plumbus ui generate` before building frontend Docker images. The typed client (`frontend/lib/client.ts`) must match the current capability schemas. If you changed entity or capability definitions without regenerating, the frontend build will fail.
|
|
309
|
-
- **Dependency resolution**: `@plumbus/core` and `@plumbus/ui` are declared as normal dependencies in `package.json`. Without the workspace overrides, `pnpm install` resolves them from the npm registry.
|
|
310
|
-
- **Volumes**: Mount persistent volumes for PostgreSQL data and the `uploads/` directory (user-generated media and exports).
|
|
311
|
-
- **Internal networking**: Frontends connect to backend via Docker service name (e.g., `http://backend:3000`). Set `API_BASE_URL` accordingly.
|
|
312
|
-
- **`NEXT_PUBLIC_API_BASE_URL`**: Set to empty string (`""`) if the frontend uses server-side proxy routes. The proxy route handler reads `API_BASE_URL` at runtime.
|
|
313
|
-
- **File layout**: Place all Docker files (Dockerfiles, compose, entrypoint, env template) in a `docker/` subdirectory. The `.dockerignore` stays at the project root (the build context). Set `context: ..` (parent) in compose `build:` sections so COPY paths resolve from the project root.
|
|
417
|
+
Place at the project root. Must include at minimum:
|
|
314
418
|
|
|
315
|
-
|
|
419
|
+
```
|
|
420
|
+
node_modules
|
|
421
|
+
.next
|
|
422
|
+
dist
|
|
423
|
+
.turbo
|
|
424
|
+
.git
|
|
425
|
+
uploads
|
|
426
|
+
**/e2e
|
|
427
|
+
docs
|
|
428
|
+
```
|
|
429
|
+
|
|
430
|
+
### File Layout
|
|
431
|
+
|
|
432
|
+
Place all Docker files in a `docker/` subdirectory:
|
|
433
|
+
|
|
434
|
+
```
|
|
435
|
+
docker/
|
|
436
|
+
Dockerfile.backend
|
|
437
|
+
Dockerfile.frontend
|
|
438
|
+
Dockerfile.admin
|
|
439
|
+
docker-compose.yml
|
|
440
|
+
entrypoint-backend.sh
|
|
441
|
+
.env.example
|
|
442
|
+
.dockerignore # at project root (the build context)
|
|
443
|
+
```
|
|
316
444
|
|
|
317
|
-
|
|
445
|
+
Set `context: ..` in compose `build:` sections so COPY paths resolve from the project root.
|
|
318
446
|
|
|
319
|
-
|
|
447
|
+
---
|
|
320
448
|
|
|
321
|
-
|
|
449
|
+
## HTTP Deployments Without TLS
|
|
450
|
+
|
|
451
|
+
`plumbus start` sets `secure: true` on auth cookies by default. If you deploy over plain HTTP (no TLS), browsers silently drop `Secure` cookies, causing infinite login redirect loops.
|
|
452
|
+
|
|
453
|
+
### Workaround
|
|
322
454
|
|
|
323
|
-
Frontend auth route handlers
|
|
455
|
+
Set `AUTH_COOKIE_SECURE=false` in the environment. Frontend auth route handlers must read it:
|
|
324
456
|
|
|
325
457
|
```ts
|
|
326
458
|
// app/api/auth/login/route.ts
|
|
@@ -335,20 +467,12 @@ cookies().set("auth_token", token, {
|
|
|
335
467
|
});
|
|
336
468
|
```
|
|
337
469
|
|
|
338
|
-
|
|
470
|
+
**Security trade-off** — always use TLS in production and set `AUTH_COOKIE_SECURE=true`.
|
|
339
471
|
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
```yaml
|
|
343
|
-
frontend:
|
|
344
|
-
environment:
|
|
345
|
-
AUTH_COOKIE_SECURE: "false" # Set to "true" behind TLS
|
|
346
|
-
```
|
|
472
|
+
---
|
|
347
473
|
|
|
348
474
|
## Kubernetes / Helm
|
|
349
475
|
|
|
350
|
-
The Docker images are self-contained and orchestrator-agnostic. When deploying to Kubernetes:
|
|
351
|
-
|
|
352
476
|
### Required Resources
|
|
353
477
|
|
|
354
478
|
| Resource | Purpose |
|
|
@@ -361,11 +485,9 @@ The Docker images are self-contained and orchestrator-agnostic. When deploying t
|
|
|
361
485
|
| `Secret` | `DB_PASSWORD`, `AUTH_SECRET`, AI provider keys |
|
|
362
486
|
| `ConfigMap` | Non-sensitive config: `DB_HOST`, `AI_DEFAULT_PROVIDER`, etc. |
|
|
363
487
|
|
|
364
|
-
### Migrations
|
|
488
|
+
### Migrations — Critical
|
|
365
489
|
|
|
366
|
-
**NEVER** run migrations in the
|
|
367
|
-
|
|
368
|
-
Run migrations as a **separate Kubernetes Job** before rolling out new pods:
|
|
490
|
+
**NEVER** run migrations in the entrypoint or in init containers that run per-replica. Run as a **separate Job** before rolling out new pods:
|
|
369
491
|
|
|
370
492
|
```yaml
|
|
371
493
|
apiVersion: batch/v1
|
|
@@ -397,28 +519,50 @@ readinessProbe:
|
|
|
397
519
|
periodSeconds: 10
|
|
398
520
|
```
|
|
399
521
|
|
|
400
|
-
### Scaling
|
|
522
|
+
### Scaling
|
|
401
523
|
|
|
402
524
|
- **Backend**: Stateless — safe to scale. All replicas must share the same `AUTH_SECRET`.
|
|
403
525
|
- **Frontends**: Stateless — scale freely behind a load balancer.
|
|
404
|
-
- **Uploads**: Replace
|
|
405
|
-
- **PostgreSQL / Redis**: Use managed services (RDS, ElastiCache
|
|
526
|
+
- **Uploads**: Replace local `uploads/` with object storage (S3, GCS) or a `ReadWriteMany` PVC.
|
|
527
|
+
- **PostgreSQL / Redis**: Use managed services (RDS, ElastiCache) — not the Docker Compose images.
|
|
528
|
+
|
|
529
|
+
---
|
|
406
530
|
|
|
407
531
|
## Production Checklist
|
|
408
532
|
|
|
409
|
-
|
|
410
|
-
|
|
533
|
+
### Server
|
|
534
|
+
|
|
535
|
+
- [ ] `plumbus start` used as production command
|
|
536
|
+
- [ ] `AUTH_SECRET` set to strong random value (32+ chars)
|
|
411
537
|
- [ ] Database SSL enabled (automatic with `plumbus start`)
|
|
412
|
-
- [ ]
|
|
413
|
-
- [ ] Health checks
|
|
414
|
-
- [ ] `
|
|
415
|
-
- [ ] Persistent volumes for PostgreSQL data and uploads
|
|
538
|
+
- [ ] Migrations run separately — NOT in entrypoint
|
|
539
|
+
- [ ] Health checks on `/ready`
|
|
540
|
+
- [ ] `config/app.config.ts` uses dynamic environment detection
|
|
416
541
|
- [ ] AI provider API keys configured
|
|
417
|
-
- [ ] Redis available for job queue
|
|
418
|
-
|
|
542
|
+
- [ ] Redis available for job queue
|
|
543
|
+
|
|
544
|
+
### Docker — Image Size
|
|
545
|
+
|
|
546
|
+
- [ ] `output: "standalone"` in all `next.config.mjs` files
|
|
547
|
+
- [ ] Frontend/admin runner copies ONLY `.next/standalone` + `.next/static`
|
|
548
|
+
- [ ] Frontend/admin runner does NOT contain `node_modules` from deps stage
|
|
549
|
+
- [ ] Backend runner uses production-only `node_modules` with `@plumbus/ui` stripped
|
|
550
|
+
- [ ] Image sizes: frontends ~150-200 MB, backend ~500-700 MB
|
|
551
|
+
|
|
552
|
+
### Docker — Build Correctness
|
|
553
|
+
|
|
554
|
+
- [ ] `.npmrc` copied into all Docker images
|
|
555
|
+
- [ ] `pnpm deploy --legacy` used to isolate frontend deps (not hoisted full-workspace install)
|
|
556
|
+
- [ ] `ENV PATH="/app/deployed/node_modules/.bin:$PATH"` set before `next build`
|
|
557
|
+
- [ ] `outputFileTracingExcludes` configured in `next.config.mjs` (typescript, @swc/core)
|
|
558
|
+
- [ ] Backend `proddeps` cleans up vitest/playwright/esbuild/typescript after npm install
|
|
419
559
|
- [ ] `.plumbus/` generated inside Docker (not copied from host)
|
|
420
|
-
- [ ]
|
|
421
|
-
- [ ] `
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
560
|
+
- [ ] `plumbus ui generate` run before building frontends
|
|
561
|
+
- [ ] `.dockerignore` excludes `node_modules/`, `dist/`, `.git/`, `uploads/`, `**/e2e/`
|
|
562
|
+
|
|
563
|
+
### Volumes and Networking
|
|
564
|
+
|
|
565
|
+
- [ ] Persistent volumes for PostgreSQL data and uploads
|
|
566
|
+
- [ ] `API_BASE_URL` set to Docker service name (e.g., `http://backend:3000`)
|
|
567
|
+
- [ ] `NEXT_PUBLIC_API_BASE_URL` set to empty string `""` (for server-side proxy)
|
|
568
|
+
- [ ] `AUTH_COOKIE_SECURE` set to `"true"` (or `"false"` only for HTTP-only — security trade-off)
|