Database Migrations
PayloadCMS uses a migration system to track and apply database schema changes. Every structural change — new fields, renamed columns, added indexes — needs a migration so the database stays in sync across environments.
When to Create Migrations
Section titled “When to Create Migrations”Create a migration when:
- Adding a new field to an existing collection
- Changing a field type
- Adding or modifying indexes
- Creating custom tables
- Transforming existing data
Do not create migrations for PayloadCMS config-only changes that do not affect the database schema.
How it works
Section titled “How it works”Each migration is a TypeScript file with two exported functions:
import { MigrateUpArgs, MigrateDownArgs, sql } from '@payloadcms/db-postgres';
export async function up({ db, payload, req }: MigrateUpArgs): Promise<void> { // Apply changes}
export async function down({ db, payload, req }: MigrateDownArgs): Promise<void> { // Rollback changes}| Function | Purpose |
|---|---|
up() | Apply the migration — runs when you deploy |
down() | Rollback the migration — runs if you need to undo |
Always write down(). It is what makes rollbacks possible when something goes wrong in production.
Creating a New Migration
Section titled “Creating a New Migration”-
Generate from the CLI
Terminal window ddev pnpm payload migrate:create migration_nameThis creates a timestamped file in
src/migrations/with theupanddownstubs ready to fill in. Prefer this over creating files manually so the timestamp is always correct. -
Write the migration
src/migrations/20260204_100000.ts import { MigrateUpArgs, MigrateDownArgs, sql } from '@payloadcms/db-postgres';export async function up({ db }: MigrateUpArgs): Promise<void> {db.execute(sql`ALTER TABLE "pages"ADD COLUMN "new_field" varchar;`);}export async function down({ db }: MigrateDownArgs): Promise<void> {db.execute(sql`ALTER TABLE "pages"DROP COLUMN "new_field";`);}The
sqltag prevents SQL injection and provides syntax highlighting in editors.down()is the exact inverse ofup()— what you add, you drop; what you rename forward, you rename back. -
Run the migration
Terminal window ddev pnpm payload migrateIn development, pending migrations also run automatically when you start
ddev pnpm dev.
Example Migrations
Section titled “Example Migrations”Adding an Index
Section titled “Adding an Index”export async function up({ db }: MigrateUpArgs): Promise<void> { db.execute(sql` CREATE INDEX "pages_new_field_idx" ON "pages" USING btree ("new_field"); `);}
export async function down({ db }: MigrateDownArgs): Promise<void> { db.execute(sql` DROP INDEX "pages_new_field_idx"; `);}Renaming a Column
Section titled “Renaming a Column”export async function up({ db }: MigrateUpArgs): Promise<void> { db.execute(sql` ALTER TABLE "pages" RENAME COLUMN "old_name" TO "new_name"; `);}
export async function down({ db }: MigrateDownArgs): Promise<void> { db.execute(sql` ALTER TABLE "pages" RENAME COLUMN "new_name" TO "old_name"; `);}Checking Migration Status
Section titled “Checking Migration Status”ddev pnpm payload migrate:statusThis lists which migrations have run and which are pending — useful before deploying to confirm the database is in the expected state.
Best Practices
Section titled “Best Practices”- Test locally first before running on production
- Always write
down()for rollback capability - Back up data before running on an existing database
- One change per migration — do not combine unrelated schema changes
- Use descriptive names —
migrate:create add_excerpt_to_postsbeatsmigrate:create update1
Troubleshooting
Section titled “Troubleshooting”Migration fails
Section titled “Migration fails”- Check the error message in the console
- Verify SQL syntax
- Ensure no conflicting migrations exist
- Run
migrate:statusto see which migrations are pending
Data loss
Section titled “Data loss”Always test migrations against a copy of the production database. Never skip down().
Circular dependencies
Section titled “Circular dependencies”If migrations fail due to table dependencies, drop tables in reverse order of creation.