CORP blue-green production deployment

Production is deployed from the current master commit in Gitea repository Robotail/Corp. The Gitea runner only opens a restricted SSH connection. Build, backup, migrations, health checks, and traffic switching run on the CORP server.

Two native slots are used instead of containers:

  • blue: backend 9001, frontend 3001;
  • green: backend 9002, frontend 3002;
  • releases: /srv/eusrr-releases/<commit-sha>;
  • slot links: /srv/eusrr-slots/{blue,green};
  • active release link: /srv/eusrr-current.

The inactive release is built and checked while the active release continues to serve users. Nginx then reloads an atomic upstream change. Existing WebSocket connections may reconnect when the old backend is drained, but HTTP traffic does not require a maintenance window. Old and new Next.js static assets are retained in /srv/eusrr-shared/next-static so already-open pages keep working.

Database migrations run before traffic switches and therefore must be backward compatible with the currently active application. Do not combine destructive schema removal with the release that stops using the schema. PostgreSQL snapshots are stored in /var/backups/eusrr/deploy for 14 days, but they are not restored automatically while the old release is accepting writes.

Bootstrap

  1. Generate a dedicated Ed25519 runner key. Never commit its private part.

  2. Copy the public key to CORP and run:

    sudo /srv/eusrr/ops/deploy/install-corp-runner-access /tmp/eusrr-gitea-runner.pub
    
  3. Store the private key as repository Actions secret CORP_DEPLOY_SSH_KEY in Gitea.

  4. Run Deploy production manually from master and verify the first blue deployment before enabling automatic master deployments.

The SSH key is forced through /usr/local/sbin/eusrr-deploy-ssh. The deploy command rejects every revision except the current Gitea origin/master SHA. Logs are appended to /var/log/eusrr-deploy.log; the active slot and successful SHA are recorded under /var/lib/eusrr-deploy.