Files
website/docs/deployment-runbook.md

5.2 KiB

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.

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:

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.