• ShipSaaSDocumentation
  • Introduction
  • Getting Started
    • Installation
    • Environment Setup
    • Database Setup
    • Development Guide
  • Database
    • Repository Pattern
    • Drizzle Queries
    • Migration Workflow
    • Drizzle Studio
    • Best Practices
  • File Management
    • File Upload Component
    • Server Actions
    • Storage Configuration
  • Authentication Methods
    • Credentials Authentication
    • Magic Link Authentication
    • Two-Factor Authentication (2FA)
    • Social Authentication
    • API Keys & Bearer Tokens
    • Organizations (Multi-tenant)
  • Authorization & Permissions
    • Roles & Hierarchy
    • Route Protection
    • API Protection
    • CASL Permissions
  • Email & Notifications
    • Resend Setup
    • React Email Templates
    • Notification System
    • Email Workflows
    • Customization & Extension
  • Stripe & Payments
    • Stripe Setup
    • Better Auth Integration
    • Checkout Methods
    • Subscription Management
    • Admin Dashboard
    • Webhook Events
  • Architecture & Development
    • Presentation Layer
    • Service Layer
    • Persistence Layer
    • Helpers Architecture
    • Project Structure
    • Development Workflow
  • Testing
    • General Testing
    • Unit Testing
    • UI Component Testing
    • Repository Testing
    • Service Testing
    • End-to-End Testing
  • Deployment
    • Vercel Deployment
    • GitHub Actions CI/CD
    • Database Backup & Restore
    • Database Migrations
    • Troubleshooting Deployment
  • AI Agents Integration
  • Blog Management
    • Blog Setup
    • MDX Blog
    • Database Blog
  • Async Tasks
    • Setup
    • Email Workflows
    • Payment Processing
    • Maintenance Jobs
  1. Documentation
  2. Deployment
  3. Database Migrations
Buydocschangelogsdemocommunity

Database Migrations

PreviousDatabase Backup & RestoreNextTroubleshooting Deployment
On this page
Migration OverviewDrizzle ConfigurationDrizzle ConfigMigration ScriptsDatabase SchemaMigration Execution in CI/CDProduction Workflow MigrationMigration Script ExecutionDevelopment Workflow1. Modify Schema Models2. Generate Migration3. Test Locally4. DeployMigration Safety

Complete guide to database migrations using Drizzle ORM in the GitHub Actions CI/CD pipeline.

Migration Overview

The SaaS boilerplate uses Drizzle ORM for database migrations with automatic execution in the CI/CD pipeline:

Migration Strategy:

  • Schema-first approach with TypeScript models
  • Automatic migration generation from schema changes
  • CI/CD integration runs migrations before deployment
  • Production safety with rollback capabilities
  • UUID extension required for PostgreSQL

Migration Flow:

  1. Modify database schema in TypeScript models
  2. Generate migration files with pnpm db:generate
  3. Test migrations locally with pnpm db:migrate
  4. Push to GitHub - CI/CD runs migrations automatically
  5. Vercel deploys with updated database schema

Drizzle Configuration

Database setup and migration configuration:

Drizzle Config

File: drizzle.config.ts

typescript
import { defineConfig } from 'drizzle-kit'
import initDotEnv from './src/db/scripts/env'

initDotEnv()

export default defineConfig({
  schema: './src/db/models/*',           // Schema location
  out: './drizzle/migrations',          // Migration output directory
  dialect: 'postgresql',                // Database type
  dbCredentials: {
    url: process.env.DATABASE_URL as string,
  },
  verbose: true,                        // Detailed migration logs
  strict: true,                         // Strict type checking
  introspect: {
    casing: 'camel',                    // Convert snake_case to camelCase
  },
})

Migration Scripts

Package.json migration commands:

json
{
  "scripts": {
    "db:generate": "drizzle-kit generate",     // Generate migration files
    "db:migrate": "drizzle-kit migrate",       // Apply migrations
    "db:push": "drizzle-kit push --force",     // Push schema directly (dev only)
    "db:pull": "drizzle-kit pull",             // Pull schema from database
    "db:drop": "drizzle-kit drop",             // Drop all tables (careful!)
    "db:studio": "drizzle-kit studio",         // Open Drizzle Studio
    "db:check": "tsx src/db/scripts/check.ts", // Check database connection
    "db:seed": "tsx src/db/scripts/seed.ts",   // Seed database with data
    "db:clear": "tsx src/db/scripts/clear.ts", // Clear all data
    "db:reset-seed": "pnpm db:clear && pnpm db:push && pnpm db:seed"
  }
}

Database Schema

The boilerplate includes a complete database schema managed by Drizzle ORM with PostgreSQL, including:

  • Authentication system with Better Auth (users, sessions, accounts, 2FA)
  • Multi-tenant organizations with roles and member management
  • Subscription and billing with Stripe integration
  • Project and task management features
  • Notification system with user preferences
  • User settings and configuration options

All schema changes are managed through Drizzle migrations and automatically applied during deployment.

Migration Execution in CI/CD

How migrations run in GitHub Actions:

Production Workflow Migration

File: .github/workflows/production.yml

- name: Install Drizzle Kit
  run: npm install -g drizzle-kit

- name: Run database migrations
  run: pnpm db:migrate
  env:
    DATABASE_URL: ${{ secrets.DATABASE_URL }}

Migration Process:

  1. Install Drizzle Kit globally in CI environment
  2. Run pnpm db:migrate with production DATABASE_URL
  3. Migrations execute against production database
  4. If migrations fail, entire deployment is blocked
  5. If migrations succeed, Vercel deployment proceeds

Migration Script Execution

File: src/db/scripts/migrate.ts

typescript
#!/usr/bin/env node
import { sql } from 'drizzle-orm'
import { drizzle } from 'drizzle-orm/node-postgres'
import { migrate } from 'drizzle-orm/node-postgres/migrator'
import pg from 'pg'

import initDotEnv from './env'
initDotEnv()

const runMigrate = async () => {
  if (!process.env.DATABASE_URL) {
    throw new Error('DATABASE_URL is not defined')
  }

  const client = new pg.Client({
    connectionString: process.env.DATABASE_URL,
  })

  await client.connect()
  const db = drizzle(client)

  console.log('⏳ Running migrations...')

  const start = Date.now()

  // Critical: Create UUID extension first
  await db.execute(sql`CREATE EXTENSION IF NOT EXISTS "uuid-ossp"`)

  // Run all pending migrations
  await migrate(db, { migrationsFolder: './drizzle/migrations' })

  const end = Date.now()

  console.log('✅ Migrations completed in', end - start, 'ms')
  process.exit(0)
}

try {
  await runMigrate()
} catch (error) {
  console.error('❌ Migration failed')
  console.error(error)
  process.exit(1)
}

Key Features:

  • UUID Extension: Creates uuid-ossp extension required for UUID primary keys
  • Error Handling: Fails gracefully with clear error messages
  • Performance Tracking: Logs migration execution time
  • Exit Codes: Proper exit codes for CI/CD pipeline

Development Workflow

Standard workflow for database changes:

1. Modify Schema Models

typescript
// Update TypeScript schema files in src/db/models/
export const users = pgTable('user', {
  // ... existing fields
  newField: text('new_field'), // Add new field
})

2. Generate Migration

bash
pnpm db:generate
# Creates migration file in drizzle/migrations/

3. Test Locally

bash
pnpm db:migrate
# Applies migration to local database

4. Deploy

bash
git add . && git commit -m "Add new field"
git push origin main
# GitHub Actions will run migration automatically

Migration Safety

The CI/CD pipeline ensures safe migrations:

  • Automatic execution during GitHub Actions
  • Production database receives delta changes only
  • Rollback capability through Vercel deployment rollback
  • Zero-downtime for additive changes (new columns, tables, indexes)

Migration best practices:

  • Add new columns as nullable first
  • Use separate migrations for breaking changes
  • Test thoroughly in preview environment before production

Your database migrations are automatically handled by the CI/CD pipeline. When you push schema changes, Drizzle will generate and execute the necessary delta migrations during deployment.