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:
[[email protected]] 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:
```
[[email protected]] 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
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 <[email protected]>
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.
Bug
The gateway production Docker image failed immediately with
ERR_MODULE_NOT_FOUNDfordotenv(and every other runtime dep). Root cause: the runner stage copied/app/node_modules— pnpm's content-addressed.pnpmstore — rather thanapps/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:
@mosaic/gateway(should be@mosaicstack/gateway)plugins/package.json files not copied beforepnpm install, so telegram/discord plugin workspace packages had nonode_modulesFix
COPY /app/node_moduleswithpnpm deploy --legacywhich produces a flat, self-contained/deploy/node_modules— no workspace symlinks or pnpm store structure needed at runtime@mosaicstack/gatewayCOPY plugins/ ./plugins/beforepnpm installso all workspace members are includedtscbuild withpnpm turbo run build --filter @mosaicstack/gateway...to build the gateway and all transitive workspace dependencies in topological order--legacyflag required by pnpm v10 for deploy in shared-lockfile workspaces withoutinject-workspace-packagesVerification
Local build: image
sha256:dd44b49b5cf49bafd91f8733792b25b08a4711d2ee1bb278460aea747c305bd0(~254 MB), all 13 packages built (13 successful, 0 failed).Runtime test:
Output:
dotenvloads cleanly. The process reaches Postgres connection — past module resolution. NoERR_MODULE_NOT_FOUND.Files changed
docker/gateway.Dockerfileonly