From brnby
Run Prisma 7 migrations inside a multi-stage Docker production image. Use when: (1) `prisma migrate deploy` crashes with "Cannot resolve environment variable: DATABASE_URL" or "cannot find module" at container startup, (2) building a production Docker image that needs to run migrations before starting the server, (3) you copied only node_modules/prisma + node_modules/@prisma but the Prisma 7 CLI still fails with MODULE_NOT_FOUND at runtime, (4) Yarn 4 node-modules linker .bin/prisma symlink breaks after Docker COPY. Covers prisma.config.ts (Prisma 7), full node_modules copy requirement, and symlink recreation pattern.
How this skill is triggered — by the user, by Claude, or both
Slash command
/brnby:prisma-7-docker-migrationsThe summary Claude sees in its skill listing — used to decide when to auto-load this skill
Prisma 7 changed how datasource URLs are configured and significantly expanded the CLI's
Prisma 7 changed how datasource URLs are configured and significantly expanded the CLI's runtime dependency closure. Two things that worked in Prisma 5/6 break silently in Prisma 7:
url = env("DATABASE_URL") in schema.prisma is no longer valid — Prisma 7 moves
this to prisma.config.ts via defineConfig().node_modules/prisma + node_modules/@prisma is not enough
— Prisma 7 CLI loads additional packages at runtime that aren't in those scopes.Additionally, Yarn 4 node-modules linker creates .bin/prisma as a symlink. Docker COPY
dereferences symlinks, silently converting the symlink to a regular file at the wrong location,
breaking WASM engine loading.
nodeLinker: node-modulesPrismaConfigEnvError: Cannot resolve environment variable: DATABASE_URLError: Cannot find module '@prisma/config'Error: Cannot find module 'effect'Error: WASM file not found or similar engine loading errorsprisma validate passes locally but fails in the containerschema.prisma no longer holds the datasource URLIn Prisma 7, datasource db { url = env("DATABASE_URL") } was removed from schema.prisma.
The URL now lives in prisma.config.ts:
// packages/server/prisma.config.ts
import { defineConfig, env } from "prisma/config";
export default defineConfig({
schema: "prisma/schema.prisma",
migrations: { path: "prisma/migrations" },
datasource: { url: env("DATABASE_URL") },
});
Prisma 7 CLI requires at runtime (beyond prisma and @prisma):
effectfast-checkpure-rand@prisma/configSelective copying of node_modules/prisma + node_modules/@prisma misses these.
.bin/ symlinksIn Yarn 4 node-modules linker, .bin/prisma is a symlink to
../prisma/build/index.js (relative path). Docker COPY follows the symlink and
copies the file content to the destination, preserving __dirname as the source
directory — not the destination. This breaks WASM loading which is relative to __dirname.
prisma.config.ts from the build stageCOPY --from=build-server /app/packages/server/prisma ./prisma
COPY --from=build-server /app/packages/server/prisma.config.ts ./prisma.config.ts
node_modules (until a deps-prod stage is available)# Prisma 7 CLI requires its full transitive dependency closure at runtime.
# TODO: Introduce a deps-prod stage (yarn workspaces focus server --production
# after adding @yarnpkg/plugin-workspace-tools) to reduce image size.
COPY --from=deps /app/node_modules ./node_modules
.bin/prisma symlink after COPY# Yarn 4 .bin/prisma is a symlink that Docker COPY dereferences. Recreate it so
# __dirname resolves to prisma/build/ for correct WASM engine loading.
RUN ln -sf /app/node_modules/prisma/build/index.js /app/node_modules/.bin/prisma
node_modules/.bin to PATH and update CMDENV PATH="/app/node_modules/.bin:$PATH"
CMD ["sh", "-c", "prisma migrate deploy && node server/index.mjs"]
prisma from devDependencies to dependenciesIn packages/server/package.json, move prisma to dependencies so the runtime
requirement is explicit and won't silently break if the install strategy changes:
{
"dependencies": {
"prisma": "7.x.x"
}
}
# ── Production ───────────────────────────────────────────────────
FROM node:22-alpine AS production
WORKDIR /app
COPY --from=build-server /app/packages/server/dist ./server
COPY --from=build-web /app/packages/web/dist ./web
# Prisma: schema + migrations + config (Prisma 7 uses prisma.config.ts for datasource URL)
COPY --from=build-server /app/packages/server/prisma ./prisma
COPY --from=build-server /app/packages/server/prisma.config.ts ./prisma.config.ts
# Prisma 7 CLI requires its full transitive dependency closure at runtime.
# TODO: Introduce a deps-prod stage (yarn workspaces focus server --production
# after adding @yarnpkg/plugin-workspace-tools) to reduce image size.
COPY --from=deps /app/node_modules ./node_modules
# Yarn 4 .bin/prisma is a symlink that Docker COPY dereferences. Recreate it so
# __dirname resolves to prisma/build/ for correct WASM engine loading.
RUN ln -sf /app/node_modules/prisma/build/index.js /app/node_modules/.bin/prisma
ENV PATH="/app/node_modules/.bin:$PATH"
ENV NODE_ENV=production
EXPOSE 4000
CMD ["sh", "-c", "prisma migrate deploy && node server/index.mjs"]
Run the production image without DATABASE_URL — it should fail with a clear
Prisma config error (not a Node.js module error):
docker build -t test:local .
docker run --rm test:local
# Expected: PrismaConfigEnvError: Cannot resolve environment variable: DATABASE_URL
# NOT: Error: Cannot find module '...'
If you see Cannot find module, the dependency closure is still incomplete.
The full node_modules copy significantly inflates the image. The correct long-term fix:
@yarnpkg/plugin-workspace-tools to Yarn pluginsdeps-prod stage: RUN yarn workspaces focus server --productionUntil then, the full copy is the reliable approach.
.ts config files are executed by the CLI's internal TypeScript loader
— tsx is NOT required in the production imagerestart: unless-stopped Docker compose policy will cause a crash-loop if
prisma migrate deploy fails — set a start_period on your healthcheck and
monitor logsprisma should be in dependencies, not devDependencies, since it runs at
container startupProvides behavioral guidelines to reduce common LLM coding mistakes, focusing on simplicity, surgical changes, assumption surfacing, and verifiable success criteria.
Searches, retrieves, and installs Agent Skills from prompts.chat registry using MCP tools like search_skills and get_skill. Activates for finding skills, browsing catalogs, or extending Claude.
npx claudepluginhub yorch/claude-skills --plugin brnby