fix(docker): pnpm deploy for self-contained gateway runtime image #488

Merged
jason.woltje merged 1 commits from fix/gateway-dockerfile-pnpm-deploy into main 2026-04-22 02:02:31 +00:00
Owner

Bug

The gateway production Docker image failed immediately with ERR_MODULE_NOT_FOUND for dotenv (and every other runtime dep). Root cause: the runner stage copied /app/node_modules — pnpm's content-addressed .pnpm store — rather than apps/gateway/node_modules, which holds the symlinks Node.js uses to resolve packages relative to the entrypoint. In a stripped image without pnpm's virtual store topology, no package was resolvable.

Additional bugs discovered during fix:

  • Wrong package filter @mosaic/gateway (should be @mosaicstack/gateway)
  • plugins/ package.json files not copied before pnpm install, so telegram/discord plugin workspace packages had no node_modules
  • No mechanism to build workspace dependency packages before the gateway

Fix

  • Replace broken COPY /app/node_modules with pnpm deploy --legacy which produces a flat, self-contained /deploy/node_modules — no workspace symlinks or pnpm store structure needed at runtime
  • Correct filter to @mosaicstack/gateway
  • Add COPY plugins/ ./plugins/ before pnpm install so all workspace members are included
  • Replace single-package tsc build with pnpm turbo run build --filter @mosaicstack/gateway... to build the gateway and all transitive workspace dependencies in topological order
  • Add --legacy flag required by pnpm v10 for deploy in shared-lockfile workspaces without inject-workspace-packages

Verification

Local build: image sha256:dd44b49b5cf49bafd91f8733792b25b08a4711d2ee1bb278460aea747c305bd0 (~254 MB), all 13 packages built (13 successful, 0 failed).

Runtime test:

docker run --rm -e MOSAIC_TIER=federated -e DATABASE_URL=postgres://nope:nope@nope:5432/nope ...

Output:

[dotenv@17.3.1] injecting env (0) from .env
TierDetectionError: [tier-detector] postgres unreachable or unusable at nope:5432

dotenv loads cleanly. The process reaches Postgres connection — past module resolution. No ERR_MODULE_NOT_FOUND.

Files changed

  • docker/gateway.Dockerfile only
## Bug The gateway production Docker image failed immediately with `ERR_MODULE_NOT_FOUND` for `dotenv` (and every other runtime dep). Root cause: the runner stage copied `/app/node_modules` — pnpm's content-addressed `.pnpm` store — rather than `apps/gateway/node_modules`, which holds the symlinks Node.js uses to resolve packages relative to the entrypoint. In a stripped image without pnpm's virtual store topology, no package was resolvable. Additional bugs discovered during fix: - Wrong package filter `@mosaic/gateway` (should be `@mosaicstack/gateway`) - `plugins/` package.json files not copied before `pnpm install`, so telegram/discord plugin workspace packages had no `node_modules` - No mechanism to build workspace dependency packages before the gateway ## Fix - Replace broken `COPY /app/node_modules` with `pnpm deploy --legacy` which produces a flat, self-contained `/deploy/node_modules` — no workspace symlinks or pnpm store structure needed at runtime - Correct filter to `@mosaicstack/gateway` - Add `COPY plugins/ ./plugins/` before `pnpm install` so all workspace members are included - Replace single-package `tsc` build with `pnpm turbo run build --filter @mosaicstack/gateway...` to build the gateway and all transitive workspace dependencies in topological order - Add `--legacy` flag required by pnpm v10 for deploy in shared-lockfile workspaces without `inject-workspace-packages` ## Verification Local build: image `sha256:dd44b49b5cf49bafd91f8733792b25b08a4711d2ee1bb278460aea747c305bd0` (~254 MB), all 13 packages built (13 successful, 0 failed). Runtime test: ``` docker run --rm -e MOSAIC_TIER=federated -e DATABASE_URL=postgres://nope:nope@nope:5432/nope ... ``` Output: ``` [dotenv@17.3.1] injecting env (0) from .env TierDetectionError: [tier-detector] postgres unreachable or unusable at nope:5432 ``` `dotenv` loads cleanly. The process reaches Postgres connection — past module resolution. No `ERR_MODULE_NOT_FOUND`. ## Files changed - `docker/gateway.Dockerfile` only
jason.woltje added 1 commit 2026-04-22 01:54:23 +00:00
fix(docker): use pnpm deploy for self-contained gateway image
All checks were successful
ci/woodpecker/pr/ci Pipeline was successful
ci/woodpecker/push/ci Pipeline was successful
cca866505f
Root cause: the runner stage copied /app/node_modules (the pnpm
content-addressed .pnpm store) instead of apps/gateway/node_modules
(which holds the symlinks Node.js uses to resolve packages relative
to the gateway entrypoint). In a stripped image without pnpm's virtual
store topology, dotenv and every other dep were unresolvable, causing
ERR_MODULE_NOT_FOUND on startup.

Fix: replace the broken node_modules copy with `pnpm deploy --legacy`
which produces a flat, self-contained node_modules at /deploy — no
workspace symlinks required. Also corrected the filter name (@mosaic/gateway
→ @mosaicstack/gateway), added COPY plugins/ to the pre-install step so
turbo's dependency graph can resolve the telegram/discord plugins that
are workspace deps of the gateway, and switched the build step to
`pnpm turbo run build --filter @mosaicstack/gateway...` so all
transitive workspace packages are compiled in topological order before
the gateway.

Verification: local build succeeded (image sha256:dd44b49b5cf49bafd91f8733792b25b08a4711d2ee1bb278460aea747c305bd0,
~254 MB). Running the image with stub env reaches Postgres connection
error (TierDetectionError: postgres unreachable at nope:5432) — dotenv
loads cleanly, past module resolution. No ERR_MODULE_NOT_FOUND.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
jason.woltje merged commit d2e408656b into main 2026-04-22 02:02:31 +00:00
Sign in to join this conversation.
No Reviewers
No Label
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: mosaicstack/stack#488