Skip to main content
Drizzle provides two workflows for applying schema changes: push for development and migrations for production.

Development vs Production

Development: db:push

Quick schema updates without migration files - perfect for rapid iteration

Production: Migrations

Versioned migration files for controlled, trackable schema changes

Available Commands

All database commands are defined in package.json:
package.json
{
  "scripts": {
    "db:push": "drizzle-kit push",
    "db:generate": "drizzle-kit generate",
    "db:migrate": "drizzle-kit migrate",
    "db:studio": "drizzle-kit studio"
  }
}

db:push

Pushes your schema directly to the database without creating migration files.
npm run db:push
When to use: Development only. Use this for rapid prototyping and local development when you want instant schema updates.
What it does:
  • Compares your src/db/schema.ts with the current database state
  • Generates and executes SQL to sync the database
  • Does NOT create migration files
  • Cannot be rolled back
Typical workflow:
1

Edit schema

Modify your schema in src/db/schema.ts
2

Push changes

npm run db:push
3

Verify

Changes are immediately applied to your database

db:generate

Generates SQL migration files from schema changes.
npm run db:generate
When to use: Production deployments. Creates versioned migration files that can be committed to version control.
What it does:
  • Compares your src/db/schema.ts with the last migration snapshot
  • Generates SQL migration files in src/db/migrations/
  • Creates a meta/ folder with migration metadata
  • Does NOT execute the migration
Example output:
src/db/migrations/
├── 0000_initial_schema.sql
├── 0001_add_post_table.sql
└── meta/
    ├── _journal.json
    └── 0001_snapshot.json

db:migrate

Executes pending migration files against the database.
npm run db:migrate
When to use: After running db:generate, or when deploying to production.
What it does:
  • Reads migration files from src/db/migrations/
  • Executes any pending migrations in order
  • Tracks which migrations have been applied
  • Can be run multiple times safely (idempotent)

db:studio

Opens Drizzle Studio - a visual database browser.
npm run db:studio
What it does:
  • Launches a web interface at https://local.drizzle.studio
  • Allows you to browse tables and data
  • Supports editing records directly
  • Useful for debugging and data inspection
Drizzle Studio runs locally and connects directly to your database. Don’t expose it to the internet.

Migration Workflows

Development Workflow (db:push)

For rapid local development:
1

Make schema changes

Edit src/db/schema.ts to add/modify tables:
export const post = pgTable("post", {
  id: text("id").primaryKey(),
  title: text("title").notNull(),
  content: text("content"),
  userId: text("user_id").notNull().references(() => user.id, { onDelete: "cascade" }),
  createdAt: timestamp("created_at").$defaultFn(() => new Date()).notNull(),
});
2

Push to database

npm run db:push
Drizzle will show you the changes before applying them.
3

Verify with Studio

npm run db:studio
Check that your tables were created correctly.

Production Workflow (Migrations)

For production-ready deployments:
1

Make schema changes

Edit src/db/schema.ts with your changes.
2

Generate migration

npm run db:generate
This creates migration files in src/db/migrations/.
3

Review migration SQL

Open the generated .sql file and verify the migration logic:
src/db/migrations/0001_add_post_table.sql
CREATE TABLE "post" (
  "id" text PRIMARY KEY NOT NULL,
  "title" text NOT NULL,
  "content" text,
  "user_id" text NOT NULL,
  "created_at" timestamp NOT NULL
);

ALTER TABLE "post" ADD CONSTRAINT "post_user_id_user_id_fk" 
  FOREIGN KEY ("user_id") REFERENCES "user"("id") ON DELETE cascade;
4

Commit migration files

git add src/db/migrations/
git commit -m "Add post table migration"
5

Run migration

On your production server:
npm run db:migrate

When to Use Each Approach

Development scenarios:
  • Local development and prototyping
  • Experimenting with schema designs
  • Personal projects without team collaboration
  • When you don’t need migration history
Advantages:
  • Instant feedback
  • No migration files to manage
  • Perfect for rapid iteration
Disadvantages:
  • No rollback capability
  • No migration history
  • Can’t track what changed when

Migration Best Practices

Before running migrations in production, open the generated .sql file and verify:
  • The SQL does what you expect
  • Foreign keys are correct
  • Indexes are created where needed
  • Data migrations won’t cause data loss
# Generate migration
npm run db:generate

# Test on local database
npm run db:migrate

# Verify with Studio
npm run db:studio
git add src/db/schema.ts src/db/migrations/
git commit -m "Add posts table with user relationship"
This keeps schema and migrations in sync in version control.
Add to your deployment pipeline:
.github/workflows/deploy.yml
- name: Run database migrations
  run: npm run db:migrate
  env:
    DATABASE_URL: ${{ secrets.DATABASE_URL }}
Always backup your production database before applying migrations:
# Backup PostgreSQL
pg_dump $DATABASE_URL > backup_$(date +%Y%m%d_%H%M%S).sql

# Then migrate
npm run db:migrate

Troubleshooting

This usually means your database state doesn’t match the migration history.Solution:
  • For development: Drop the database and re-run all migrations
  • For production: Manually adjust the migration or database state
Drizzle may not detect certain changes automatically.Solution:
  • Use migrations instead of push for complex changes
  • Verify your schema syntax is correct
Check your DATABASE_URL environment variable.Solution:
# Verify it's set
echo $DATABASE_URL

# Should look like:
# postgresql://user:password@localhost:5432/dbname

Next Steps

Adding Tables

Learn how to add custom tables to your schema

Schema Reference

View the complete database schema

Drizzle Docs

Official migration documentation

Build docs developers (and LLMs) love