Prisma Seed Script Not Running in CI/CD Pipeline

Prisma seed script not running in CI/CD almost never means the seed file itself has a logic bug — it runs fine locally, inserting exactly the reference data you expect. In CI, it either fails outright with an error about a missing command, or worse, does nothing at all with no error, leaving a freshly migrated database with no data and a pipeline that reports green. Both symptoms trace back to the same root cause: something environment-specific that never shows up when you're running the seed script on your own machine.
Short answer: check whether your CI pipeline actually calls prisma db seed explicitly — current Prisma versions no longer trigger seeding automatically as part of migrate dev or migrate deploy — and separately confirm the TypeScript runner your seed command depends on (tsx, or the older ts-node) is actually installed in the CI environment, not just on your laptop. Two different failure modes, both invisible until you look at exactly what the CI job runs versus what you assumed it ran.

Why Prisma's Seed Step Silently Gets Skipped in CI
The quieter of the two failure modes is the more common one: your pipeline was written assuming prisma migrate deploy (or migrate dev, in an older setup) would seed the database as a side effect, because that used to be true. Current Prisma versions deliberately removed that coupling — seeding is only triggered explicitly by running npx prisma db seed, and running a migration no longer does it for you. If your CI YAML has a prisma migrate deploy step and nothing calling db seed afterward, the pipeline completes successfully, reports no errors, and simply never seeds anything — because nothing in the pipeline ever asked it to.
(If you've been re-reading your seed script for a bug that would explain "nothing happened" — there isn't one to find. A script that's never invoked doesn't fail. It just doesn't run.)
Fix 1: Make db seed an Explicit CI Step, in the Right Order
The direct fix is adding the seed command explicitly, after migrations have actually completed:
1# .github/workflows/deploy.yml
2- name: Run database migrations
3 run: npx prisma migrate deploy
4
5- name: Seed the database
6 run: npx prisma db seedOrder matters here specifically because a seed script assumes the schema it's inserting data against already exists — running seed before or in parallel with migrations means it's writing against tables and columns that may not be fully created yet, which produces errors that look like a broken seed script but are actually a race condition in step ordering.
Fix 2: Configure the Seed Command Correctly, and Confirm the Runner Is Actually Installed
Prisma's current seeding documentation configures the seed command directly in prisma.config.ts rather than an older package.json field, and recommends tsx over ts-node:
1// prisma.config.ts
2import 'dotenv/config';
3import { defineConfig } from 'prisma/config';
4
5export default defineConfig({
6 schema: 'prisma/schema.prisma',
7 migrations: {
8 path: 'prisma/migrations',
9 seed: 'tsx prisma/seed.ts',
10 },
11});The classic version of this bug — ts-node ENOENT or an equivalent "command not found" for whichever runner you're using — happens when that runner is listed only under devDependencies, and CI installs with a flag that skips dev dependencies for a production build. Prisma's own GitHub discussion on this exact ts-node/seed interaction has several teams' variations of the same root cause if your specific error text doesn't match what's shown here. The fix is either installing with dev dependencies included for the job that runs seeding, or making sure tsx (or ts-node) is available wherever prisma db seed actually executes:
1// package.json
2{
3 "devDependencies": {
4 "tsx": "^4.0.0"
5 }
6}1# CI install step — include dev dependencies if the seed step needs tsx to exist
2npm ci --include=devnpm's own npm ci reference documents exactly what --include=dev and its related flags control, worth a direct check if your CI install step was copied from a production-optimized template that predates adding a seed step at all.

Fix 3: Make the Seed Script Idempotent
A seed script that assumes a completely empty database breaks the moment CI runs against a persistent environment — a shared staging database, for instance — that already has seed data from a previous run:
1// prisma/seed.ts — idempotent: safe to run once or a dozen times
2import { PrismaClient } from '@prisma/client';
3
4const prisma = new PrismaClient();
5
6async function main() {
7 await prisma.plan.upsert({
8 where: { slug: 'starter' },
9 update: {},
10 create: { slug: 'starter', name: 'Starter', priceMonthly: 2900 },
11 });
12}
13
14main()
15 .catch((error) => {
16 console.error(error);
17 process.exit(1);
18 })
19 .finally(async () => {
20 await prisma.$disconnect();
21 });upsert() in place of create() means the same seed run against a database that already has this row updates it harmlessly instead of throwing a unique-constraint violation — which is exactly the behavior a CI/CD pipeline needs from a step that might run against the same environment more than once.

The Opinion Part
Here's the pattern worth naming, because it's the quiet theme running through nearly every bug in this Prisma sub-series: a tool's behavior changing between major versions is a completely reasonable thing for a maintainer to do, and it's also exactly the kind of change that turns a pipeline written two years ago into one silently doing less than the team believes it's doing. Teams already spend an estimated 33-42% of their time servicing technical debt rather than shipping features (Stripe Developer Coefficient) — a CI pipeline that assumes yesterday's tool behavior is precisely the kind of debt that doesn't show up on any dashboard until someone notices a staging environment has been running with no reference data for months. The fix isn't just adding the missing step. It's treating "does our pipeline still do what we think it does" as a question worth actually re-checking after any major dependency upgrade, not just after something visibly breaks.
Conclusion
If Prisma's seed script isn't running in CI, check two things before touching the seed file itself: whether your pipeline explicitly calls prisma db seed at all now that migrations no longer trigger it automatically, and whether the TypeScript runner the seed command depends on is actually installed in the environment executing it. Get the order right — migrate first, seed second — and make the seed script idempotent so it survives running against an environment that isn't perfectly empty.
If migrations themselves are the part still causing CI trouble on a managed database, our shadow database permission guide and our migration drift guide both cover adjacent failure modes in the same pipeline.
Add the explicit seed step, install the runner where CI actually needs it, and watch a fresh environment come up with real reference data instead of an empty, technically-successful deploy.
Frequently Asked Questions
In current Prisma versions, seeding is deliberately decoupled from prisma migrate — running migrate dev or migrate deploy no longer automatically triggers a seed the way older versions did. If your CI pipeline was written assuming migrate would seed the database as a side effect, it simply won't anymore, and unless you add an explicit npx prisma db seed step, nothing happens and nothing errors either, because the pipeline never asked for it.
It means the TypeScript runner your seed command depends on isn't actually installed in the CI environment executing it — commonly because ts-node (or its current recommended replacement, tsx) was only ever installed as a devDependency, and the CI job's install step skipped dev dependencies. The seed command references a binary that plain doesn't exist in that environment, which fails immediately rather than running anything.
Prisma's own current documentation recommends tsx over ts-node for running a TypeScript seed file, configured directly in prisma.config.ts rather than a package.json field. tsx tends to have fewer of the ESM/CommonJS interop issues that historically caused ts-node-specific seed failures, though the underlying 'is the runner actually installed where CI expects it' problem applies to either choice equally.
A seed script almost always assumes the schema it's inserting data against already exists — tables, columns, and constraints created by your migrations. Running seed and migrate in parallel, or seed before migrate completes, means the seed script is writing against a schema that may not be fully in place yet, producing errors that look like a seed script bug but are actually a race condition in pipeline ordering.
Yes, strongly recommended. A CI pipeline that runs against a database that already has seed data from a previous run — a persistent staging environment, for instance — will fail on unique constraint violations if the seed script uses create() everywhere. Writing the seed script with upsert() instead means running it once or a dozen times produces the same result, which is what a CI/CD pipeline actually needs from a repeatable step.
