Budgetron uses Drizzle ORM for database management and migrations. This guide covers the migration workflow for local development and production.
Migration Files Location
All migrations are stored in the drizzle/migrations/ directory:
drizzle/
migrations/
0000_init.sql
0001_seed-categories.sql
0002_seed-feature-flags-allow-signup.sql
0003_use-single-display-name.sql
...
meta/
_journal.json
0000_snapshot.json
0001_snapshot.json
...
migrate/ # Standalone migration runner
migrate.ts
package.json
Configuration
Migration settings are defined in drizzle.config.ts:
import { defineConfig } from 'drizzle-kit'
export default defineConfig({
dialect: 'postgresql',
dbCredentials: {
url: process.env.DB_URL!,
ssl: false,
},
out: './drizzle/migrations', // Migration output directory
schema: './src/server/db/schema.ts', // Schema source
casing: 'snake_case', // Database naming convention
strict: true,
migrations: {
schema: 'public',
table: '__drizzle_migrations', // Migration tracking table
},
})
Database Schema Organization
The database schema is defined in TypeScript files:
- src/server/db/schema.ts - Central export that combines all schemas
- src/server/db/schemas/ - Individual table definitions:
user.ts, session.ts, account.ts - Authentication
bank-account.ts - Bank accounts
transaction.ts - Financial transactions
category.ts - Categories
budget.ts - Budgets
currency-rate.ts - Exchange rates
group.ts, group-user.ts - Groups
user-settings.ts - User settings
feature-flags.ts - Feature flags
enums.ts - Shared enums
Common Migration Commands
Apply Migrations
Run pending migrations against your database:
This command:
- Connects to the database specified in
DB_URL
- Checks the
__drizzle_migrations table
- Applies any pending migrations in order
Generate New Migration
After modifying schema files in src/server/db/schemas/, generate a migration:
Drizzle Kit will:
- Compare your schema to the current database state
- Generate SQL migration files
- Update the metadata in
drizzle/migrations/meta/
Review the generated SQL before applying it.
Push Schema (Development Only)
For rapid prototyping, push schema changes directly without migrations:
Use with caution: db:push bypasses migration history and can cause data loss. Only use during active development.
Open Drizzle Studio
Explore your database with Drizzle Studio (a visual database browser):
This opens a web interface at http://localhost:4983.
Development Environment Workflow
If you maintain separate .env and .env.development files:
Using Development Environment
# Generate migration using .env.development
pnpm run db:generate:dev
# Apply migrations to development database
pnpm run db:migrate:dev
# Push schema to development database
pnpm run db:push:dev
# Open Studio for development database
pnpm run db:studio:dev
These commands use dotenv-cli to load variables from .env.development instead of .env.
Creating Migrations
Step-by-Step Workflow
-
Modify the schema
Edit files in
src/server/db/schemas/, for example transaction.ts:
export const transactions = pgTable('transaction', {
id: serial('id').primaryKey(),
description: text('description').notNull(),
amount: decimal('amount', { precision: 10, scale: 2 }).notNull(),
// Add new column
notes: text('notes'),
})
-
Generate the migration
Drizzle Kit will prompt you for a migration name or auto-generate one.
-
Review the migration
Check the generated SQL in
drizzle/migrations/[number]_[name].sql:
ALTER TABLE "transaction" ADD COLUMN "notes" text;
-
Apply the migration
-
Commit both the schema and migration files
git add src/server/db/schemas/transaction.ts
git add drizzle/migrations/
git commit -m "Add notes column to transactions"
Patching Migrations
For advanced scenarios, you can patch migrations programmatically:
pnpm run db:patch-migrations
# Or for development environment
pnpm run db:patch-migrations:dev
This runs scripts/db-migrations-patch.ts, which can modify migration files before they’re applied.
Production Migrations
Docker Entrypoint
The Dockerfile includes an entrypoint script that automatically runs migrations on container start:
# From entrypoint.sh
./drizzle/migrate/node_modules/.bin/tsx \
./drizzle/migrate/migrate.ts \
--db-url="$DB_URL" \
--migrations-folder=./drizzle/migrations
Manual Production Migrations
If you need to run migrations manually in production:
DB_URL="postgres://user:pass@host:5432/db" pnpm run db:migrate
Or use the standalone migration runner:
cd drizzle/migrate
node migrate.js \
--db-url="postgres://user:pass@host:5432/db" \
--migrations-folder=../migrations
Migration Tracking
Drizzle tracks applied migrations in the __drizzle_migrations table:
| Column | Type | Description |
|---|
| id | serial | Auto-incrementing ID |
| hash | text | Migration file hash |
| created_at | timestamp | When migration was applied |
You can query this table to see migration history:
SELECT * FROM __drizzle_migrations ORDER BY created_at DESC;
Best Practices
Do:
- Always generate migrations for schema changes in production
- Review generated SQL before applying migrations
- Test migrations on a copy of production data
- Commit migrations with schema changes in the same PR
- Use descriptive migration names when prompted
- Back up production database before major migrations
Don’t:
- Don’t use
db:push in production - it bypasses migration history
- Don’t edit applied migrations - create a new migration instead
- Don’t skip migrations - apply them in order
- Don’t commit schema changes without migrations
Troubleshooting
Migration Already Applied
If you see “Migration already applied”:
- Check
__drizzle_migrations table
- Verify the migration hash matches
- If developing locally, consider resetting your database
Migration Failed
If a migration fails mid-execution:
- Check PostgreSQL logs for errors
- Verify your database user has sufficient permissions
- Manually fix the database state if needed
- Remove the failed entry from
__drizzle_migrations
- Re-run the migration
Schema Out of Sync
If your schema doesn’t match the database:
- Run
pnpm run db:generate to see the diff
- Apply with
pnpm run db:migrate
- Or use
pnpm run db:push if in development