2026-04-28 01:09:17 +08:00
# Deployment Runbook
This runbook is the source of truth for taking the Eversolo Payload/Next.js site from a local database and media folder to a production deployment.
2026-04-28 09:04:03 +08:00
For the command-level Chinese handoff guide, see `docs/local-db-to-production-deployment.md` .
2026-04-28 01:09:17 +08:00
## Release Inputs
- Git commit to deploy.
- PostgreSQL database dump exported from the approved local database.
- `media/` and `files/` directories exported from the same local workspace as the database dump.
- Production environment variables from `.env.example` , filled with production values.
## Required Environment
Use Node.js 22 LTS or newer. The project declares `>=20.9.0` , but production should use one pinned LTS line.
Required variables:
- `NEXT_PUBLIC_SITE_URL` : public canonical origin, for example `https://www.eversolo.com` .
- `PAYLOAD_SECRET` : long random secret, never use the example value.
- `DATABASE_URI` : PostgreSQL connection string.
- `PAYLOAD_DB_PUSH=false` : production must use migrations, not schema push.
- `PAYLOAD_RUN_MIGRATIONS_ON_START=false` : production startup should not run migrations interactively.
Optional variables:
- `PORT` : runtime port for `next start` .
- `SMOKE_BASE_URL` : base URL used by `npm run smoke` .
## Deployment Steps
2026-04-28 09:04:03 +08:00
1. Run local release checks: `npm run lint` , `npm run typecheck` , `npm run build` , and `npm run payload:migrate:status` .
2. Load the local release environment with `set -a; source .env; set +a` , then confirm the local release database has no dev marker:
`psql "$DATABASE_URI" -Atc "select count(*) from payload_migrations where batch = -1;"` .
3. Export the approved PostgreSQL database:
`pg_dump --format=custom --no-owner --no-acl --file=eversolo.payload.dump "$DATABASE_URI"` .
4. Export matching uploads:
`tar -czf eversolo.uploads.tgz media files` .
5. Restore the approved PostgreSQL dump into the production database with `pg_restore --no-owner --no-acl` .
6. Copy `media/` and `files/` to the production persistent upload volume.
7. Install dependencies with `npm ci` .
8. Run `npm run generate:types` only during build validation, not as a production data mutation step.
9. Run `npm run typecheck` .
10. Run `npm run build` .
11. Run `npm run payload:migrate` .
12. Run `npm run payload:migrate:status` and confirm every migration is `Yes` .
13. Start the app with `npm run start` .
14. Run `npm run smoke -- "$NEXT_PUBLIC_SITE_URL"` .
2026-04-28 09:16:15 +08:00
15. Log out of the admin, open `/admin/login` , and confirm the verification code is required before password validation.
2026-04-28 01:09:17 +08:00
## Data Rules
- Do not run `PAYLOAD_DB_PUSH=true` against production.
- Do not rely on application startup to run migrations. Use the explicit migration step.
- Before exporting a local database for production, confirm `payload_migrations` has no `batch = -1` dev marker. That marker means schema push history is still recorded and production migration commands can prompt interactively.
- Do not edit migrations after they have shipped to production. Add a new forward migration instead.
- Keep schema migrations and data backfills separate. Backfill scripts in `src/scripts/` are manual operational tools, not automatic boot steps.
- The deployed database and upload directories must come from the same local export, otherwise media relationships can point at missing files.
2026-04-28 09:16:15 +08:00
- Admin login captcha is stateless and signed with `PAYLOAD_SECRET` ; changing that secret invalidates existing login sessions and outstanding captcha tokens.
2026-04-28 01:09:17 +08:00
## Rollback
Rollback means switching code and data together:
1. Stop traffic or move traffic back to the previous deployment.
2. Restore the previous database dump.
3. Restore the matching previous `media/` and `files/` directories.
4. Deploy the previous Git commit.
5. Re-run smoke checks before reopening traffic.
Do not run migration down scripts on production as the default rollback path.