Skip to main content

Zero-Downtime Static Deployment

This guide adds a two-stage deployment flow for this Docusaurus site:

  1. Build and stage a new release without touching current traffic.
  2. Switch traffic in one atomic symlink update (typically well under one second).

This approach avoids the downtime caused by rebuilding or replacing the same live build/ folder.

What Was Added

  • Deploy script: scripts/deploy-static-release.sh
  • Rollback script: scripts/rollback-static-release.sh
  • Nginx example: deployment/nginx/aeef-static.conf.example
  • Manual deploy workflow: .github/workflows/deploy-static.yml

GitHub Actions Workflow Dispatch

Use the Deploy Static Site workflow (.github/workflows/deploy-static.yml) with:

  • action=stage to build and upload a release without switching traffic
  • action=switch to atomically move current to a staged release
  • action=rollback to switch back to previous (or a specific release id)

Required Repository Secrets

  • DEPLOY_HOST — production host/IP
  • DEPLOY_USER — SSH user for deployment
  • DEPLOY_SSH_KEY — private key for that user

Optional Repository Secrets

  • DEPLOY_PORT — defaults to 22
  • DEPLOY_KNOWN_HOSTS — full known_hosts line for strict host verification
  • FEEDBACK_ENDPOINT or FEEDBACK_EMAIL — injected at build time
  1. Run workflow with action=stage.
  2. Confirm staged release exists and smoke check it on host.
  3. Run workflow with action=switch and the same release_id.
  4. If needed, run action=rollback.

Directory Layout

/var/www/aeef/
releases/
20260228153045-a1b2c3d/
20260228160750-e4f5g6h/
current -> /var/www/aeef/releases/20260228160750-e4f5g6h
previous -> /var/www/aeef/releases/20260228153045-a1b2c3d

Nginx should serve /var/www/aeef/current.

One-Time Nginx Setup

Use deployment/nginx/aeef-static.conf.example as your server block template and point root to /var/www/aeef/current.

Stage a Release (No Traffic Impact)

From repo root:

scripts/deploy-static-release.sh --install-deps

This builds and copies artifacts into /var/www/aeef/releases/<release-id> but does not switch live traffic.

Atomic Cutover (Live Switch)

When ready:

scripts/deploy-static-release.sh --skip-build --release-id <release-id> --switch --reload-nginx

If you do not pass --release-id, the script stages a new release id and switches that release.

Fast Rollback

Rollback to the previous symlink target:

scripts/rollback-static-release.sh --reload-nginx

Or roll back to a specific release:

scripts/rollback-static-release.sh --to-release <release-id> --reload-nginx

Suggested Deployment Sequence

  1. Run CI checks (typecheck, docs checks, build).
  2. Stage release on server (deploy-static-release.sh --install-deps).
  3. Smoke test staged release path locally on server if needed.
  4. Switch symlink (--switch --reload-nginx).
  5. Monitor errors and latency for 5-10 minutes.
  6. Roll back immediately if needed.

Operational Notes

  • Keep build and switch as separate steps.
  • Do not let runtime processes serve from a mutable build directory.
  • Keep at least one known-good release for instant rollback.