Skip to content

Production Host Nginx cutover runbook

This runbook describes a candidate configuration and an operator-controlled cutover for vayzomart.com. It does not authorize a deployment, edit the live host, obtain certificates, or switch public traffic. scripts/deploy-production-vps.sh deliberately leaves Host Nginx unchanged.

Fixed routing contract

Request Loopback upstream
https://vayzomart.com/ and https://www.vayzomart.com/ frontend 127.0.0.1:3300
https://vayzomart.com/api/ API 127.0.0.1:3301/api/
/api/health/ready and /health/live API 127.0.0.1:3301

Only the production /api/ location permits client_max_body_size 100m;. Unknown HTTP and HTTPS Host headers are rejected. Staging remains independently routed to ports 3200/3201.

Mandatory sequence

  1. Provision the production stack without changing Host Nginx or public traffic.
  2. Certify the production frontend/API through loopback ports and verify Data Protection persistence.
  3. Complete PayU LIVE readiness / preflight: onboarding, settlement account, protected LIVE credentials, endpoint/callback/webhook configuration, DNS/TLS prerequisites, local hash and validation tests, reconciliation configuration, and safe outbound verification connectivity. This is not inbound external callback, browser-return, or webhook E2E certification. Razorpay remains disabled and is not a fallback for V1; PayU automated refunds remain disabled. Any future Razorpay re-enable is a separate business decision and certification exercise.
  4. Confirm every legal, CA, business, security, backup, and release gate.
  5. Render and validate a Host Nginx candidate without switching traffic.
  6. Obtain explicit CUTOVER AUTHORIZATION tied to the exact release and candidate digest.
  7. Back up the active Host Nginx file, install the candidate, and require nginx -t to pass before reload. If it fails, follow the pre-reload restore procedure below and do not reload.
  8. Reload only the validated configuration, then run external TLS, frontend, API-readiness, and upload-limit smoke checks.
  9. Run the controlled post-cutover PayU LIVE external certification below.
  10. Confirm final launch only after both external application smoke and payment certification pass. Cutover alone is not final launch approval.

Steps 7 through 10 are prohibited until step 6 is recorded. This repository does not automate them.

Render a candidate (non-deploying)

Run from a trusted checkout of the authorized release. Certificate files must already exist and must cover both production names; this runbook never runs Certbot.

export PRODUCTION_FRONTEND_HOST='vayzomart.com'
export PRODUCTION_FRONTEND_PORT='3300'
export PRODUCTION_API_PORT='3301'
export PRODUCTION_TLS_CERTIFICATE_PATH='/absolute/path/to/existing/fullchain.pem'
export PRODUCTION_TLS_CERTIFICATE_KEY_PATH='/absolute/path/to/existing/privkey.pem'

envsubst '${PRODUCTION_FRONTEND_HOST} ${PRODUCTION_FRONTEND_PORT} ${PRODUCTION_API_PORT} ${PRODUCTION_TLS_CERTIFICATE_PATH} ${PRODUCTION_TLS_CERTIFICATE_KEY_PATH}' \
  < infra/nginx/nginx-vps-production.conf.template \
  > /tmp/vayzomart-production.conf.candidate

Inspect the rendered file for unresolved ${...} tokens and verify its digest against the cutover record. Do not print or copy private-key contents.

Local disposable syntax proof

bash scripts/test-production-nginx-template.sh

The test generates a disposable certificate and runs nginx -t inside an isolated Docker container with networking disabled. It does not access /etc/nginx, staging, or production.

Authorized host procedure (reference only)

After explicit cutover authorization, the operator must first verify both loopback upstreams, validate the candidate against the host's installed Nginx, and capture the active configuration. The exact active site path is host-specific and must be confirmed rather than assumed.

curl --fail --silent --show-error http://127.0.0.1:3300/
curl --fail --silent --show-error http://127.0.0.1:3301/health/ready
sudo nginx -t -c /etc/nginx/nginx.conf

# Replace these placeholders only after confirming the actual live paths.
sudo cp --preserve=all <ACTIVE_SITE_FILE> <TIMESTAMPED_BACKUP_FILE>
sudo install -o root -g root -m 0644 /tmp/vayzomart-production.conf.candidate <ACTIVE_SITE_FILE>
sudo nginx -t
sudo systemctl reload nginx

The reload command is permitted only when the immediately preceding nginx -t succeeds.

Candidate fails validation before reload

If the newly installed candidate fails nginx -t before reload:

  1. Do not reload Nginx.
  2. Immediately restore the captured pre-cutover Host Nginx file to the active path.
  3. Run nginx -t again against the restored configuration.
  4. Leave the currently running routing unchanged; existing Nginx workers continue using the pre-cutover configuration because no reload occurred.
  5. Stop the cutover and investigate. If the restored file does not validate, do not reload and escalate for manual recovery.

This pre-reload restore is distinct from routing rollback after a successful reload followed by failed external verification.

Then verify HTTPS for both names, the two health routes, representative API behavior, and upload limits. Do not expose secrets in command output or logs. Passing these checks is not final launch approval; controlled PayU LIVE external certification is still required.

PayU LIVE external certification after cutover

Use only an explicitly authorized controlled production smoke transaction. Verify Hosted Checkout initiation; browser success/failure return delivery; response/reverse-hash validation; delivery to POST /api/webhooks/payu; authoritative verify_payment provider verification; duplicate-webhook and callback/webhook-race idempotency; reconciliation; exactly-once order/payment finalization; and that Razorpay remains disabled.

Do not authorize automated refunds. If certification fails, stop launch, do not announce production as ready, and choose the documented Host Nginx routing rollback and/or canonical application rollback based on the failure. Public cutover alone never constitutes final launch approval.

Routing rollback

If external post-reload verification or controlled payment certification fails and the decision is to reverse public routing, restore the exact timestamped Host Nginx backup, validate it, and reload. Do not improvise a new configuration during an incident.

sudo install -o root -g root -m 0644 <TIMESTAMPED_BACKUP_FILE> <ACTIVE_SITE_FILE>
sudo nginx -t
sudo systemctl reload nginx

Restoring Host Nginx reverses only the traffic switch. If the production application stack also needs rollback, follow its separately gated rollback procedure. Do not point production traffic at staging ports or reuse staging certificates/configuration.