100 lines
3.1 KiB
Markdown
100 lines
3.1 KiB
Markdown
---
|
|
name: payload-cms-push-dev-prod
|
|
description: Payload CMS postgresAdapter push:false vs push:true — conditional schema auto-push for dev, migration-only for prod
|
|
type: concept
|
|
---
|
|
|
|
# Payload CMS — `push` Config for `postgresAdapter` (Dev vs Prod)
|
|
|
|
## What `push` Does
|
|
|
|
In `payload.config.ts`, `postgresAdapter` has a `push` option:
|
|
|
|
```ts
|
|
import { postgresAdapter } from '@payloadcms/db-postgres'
|
|
|
|
export default buildConfig({
|
|
db: postgresAdapter({
|
|
pool: { connectionString: process.env.DATABASE_URI },
|
|
push: true, // or false
|
|
}),
|
|
})
|
|
```
|
|
|
|
- **`push: true`** — Payload calls `drizzle-kit push` at startup, auto-syncing the schema to the DB. No migrations needed. Schema changes are applied immediately on every restart.
|
|
- **`push: false`** (default) — Payload uses `drizzle-kit migrate` style. Schema is only updated by running explicit migration files in `migrationDir`.
|
|
|
|
## 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
|
|
|
|
```ts
|
|
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
|
|
|
|
```bash
|
|
# 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
|
|
# Dockerfile
|
|
CMD ["sh", "-c", "npx payload migrate && node server.js"]
|
|
```
|
|
|
|
Or in `docker-compose.yml`:
|
|
|
|
```yaml
|
|
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
|