3.1 KiB
| name | description | type |
|---|---|---|
| payload-cms-push-dev-prod | Payload CMS postgresAdapter push:false vs push:true — conditional schema auto-push for dev, migration-only for prod | concept |
Payload CMS — push Config for postgresAdapter (Dev vs Prod)
What push Does
In payload.config.ts, postgresAdapter has a push option:
import { postgresAdapter } from '@payloadcms/db-postgres'
export default buildConfig({
db: postgresAdapter({
pool: { connectionString: process.env.DATABASE_URI },
push: true, // or false
}),
})
push: true— Payload callsdrizzle-kit pushat startup, auto-syncing the schema to the DB. No migrations needed. Schema changes are applied immediately on every restart.push: false(default) — Payload usesdrizzle-kit migratestyle. Schema is only updated by running explicit migration files inmigrationDir.
The Critical Gotcha
push defaults to false.
After wiping the database volume (e.g., during development or staging reset), push: false means tables are never created. The admin panel returns SQL errors:
relation "users" does not exist
relation "payload_preferences" does not exist
This is a silent failure — there are no startup errors, just 500s when the admin panel tries to query tables that don't exist.
The Correct Pattern
postgresAdapter({
pool: { connectionString: process.env.DATABASE_URI },
// Auto-push in dev (no migration workflow needed)
// Migration-only in prod (controlled, auditable, reversible)
push: process.env.NODE_ENV === 'development',
migrationDir: './migrations',
})
Why Not Always push: true?
In production push: true means every deployment auto-alters the database schema. This is dangerous because:
- Column renames/drops are immediate and irreversible on the running DB
- No migration history → can't rollback schema changes
- Concurrent deployments can race on schema changes
Why Not Always push: false?
In development push: false requires running npx payload migrate after every collection change. This adds friction and is easy to forget, leading to confusing "table not found" errors after volume resets.
Running Migrations in Production
# Generate a migration from current schema changes
npx payload migrate:create --name add-status-to-leads
# Apply all pending migrations
npx payload migrate
# Check migration status
npx payload migrate:status
Docker Compose Entrypoint Pattern
Run migrations before starting the server in production:
# Dockerfile
CMD ["sh", "-c", "npx payload migrate && node server.js"]
Or in docker-compose.yml:
services:
web:
command: sh -c "npx payload migrate && node .next/standalone/server.js"
depends_on:
db:
condition: service_healthy
Related Scaffold Issue
When generating a new Payload + Next.js project, the scaffold (as of Payload 3.x) may omit push: false in the production config template. Always check payload.config.ts before deploying to production.
Source: daily/2026-05-09.md | 2026-05-09