Files
website/docs/deployment-runbook.md
T

105 lines
5.2 KiB
Markdown

# 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.
For the command-level Chinese handoff guide, see `docs/local-db-to-production-deployment.md`.
## Release Inputs
- Git commit to deploy.
- PostgreSQL database dump exported from the approved local database.
- `media/`, `files/`, and `videos/` 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
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 videos`.
5. Restore the approved PostgreSQL dump into the production database with `pg_restore --no-owner --no-acl`.
6. Copy `media/`, `files/`, and `videos/` 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"`.
15. Log out of the admin, open `/admin/login`, and confirm the verification code is required before password validation.
## Deployment Package
Use `scripts/package-deploy.sh` when a server-ready source and build archive is needed.
```bash
NEXT_PUBLIC_SITE_URL=https://www.eversolo.com ./scripts/package-deploy.sh
```
The script runs `npm run build`, creates `deploy-packages/<timestamp>-deploy-nopublic-noenv/eversoloweb-deploy-nopublic-noenv-<timestamp>.tar.gz`, and writes a matching `manifest.txt`.
The archive intentionally excludes:
- `public/`
- upload/data directories that are deployed separately: `media/`, `files/`, and `videos/`
- `.env`, `.env.*`, and `.env.example`
- `node_modules/`
- `.git/`
- `deploy-packages/`
- `.next/cache/` and `.next-dev/`
- macOS metadata inside the archive, such as `._*`, `.DS_Store`, and `__MACOSX`
- common local logs, cache folders, test output, and temporary files
On APFS, macOS may still attach `com.apple.provenance` to the outer `.tar.gz` file even after `xattr -c`. The script prints that as a warning, but still fails if Apple metadata appears inside the archive contents.
`NEXT_PUBLIC_SITE_URL` must be the final public origin when the package is built. Do not build a deployment package with `localhost`, because sitemap, robots, canonical URLs, and metadata can be generated from this value.
If the build has already been run with the correct public URL and only the tarball needs to be recreated:
```bash
NEXT_PUBLIC_SITE_URL=https://www.eversolo.com SKIP_BUILD=1 ./scripts/package-deploy.sh
```
## 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/video/file relationships can point at missing files.
- Admin login captcha is stateless and signed with `PAYLOAD_SECRET`; changing that secret invalidates existing login sessions and outstanding captcha tokens.
## 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/`, `files/`, and `videos/` 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.