Maintenance mode
Tiered write-freeze across backend, web, and workers — for Supabase region cutover and other maintenance windows.
Connect your agent today
Draft from chat, review in your calendar, and publish only what you approve.
Overview
MAINTENANCE_MODE controls a **tiered** maintenance flag shared by the **API** (/backend), **web** (/web), and **BullMQ workers** (/orchestrator). Use it to stop database writes during a Supabase region cutover or other short ops window without taking down public marketing pages, docs, or blog content.| Mode | API | Web UI | Workers |
|---|---|---|---|
| off | Normal | Normal | Normal |
| banner | Normal | Optional notice on public layouts | Normal |
| freeze_writes | Block mutations (503) | Redirect auth/app routes to /maintenance | Exit on startup (no job consumption) |
Example templates:
- backend/.env.development.example
- web/.env.production.example
- orchestrator/.env.production.example
- infra/self-host/.env.example
Warning
During a cutover, deploy MAINTENANCE_MODE=freeze_writes on all three surfaces — Vercel backend, Vercel web, and Railway workers (or self-host API, web, and worker containers). If workers keep running while the API is frozen, scheduled posts and notification jobs can still mutate Postgres.
Environment variables
| Variable | Where | Purpose |
|---|---|---|
| MAINTENANCE_MODE | Backend, web (server), workers | off, banner, or freeze_writes |
| MAINTENANCE_RETRY_AFTER_SECONDS | Backend only | Retry-After header on blocked API mutations (default 3600) |
| MAINTENANCE_BYPASS_SECRET | Backend only | Optional operator header X-Maintenance-Bypass for smoke tests during freeze_writes |
On the web app, MAINTENANCE_MODE is server-only (not a VITE_* variable). Set it in web/.env.production.local.
Workers read the same keys through backend/config/GlobalConfig.ts (orchestrator imports backend config). Keep worker env aligned with the API.
What stays live during freeze_writes
Public SEO and CMS reads
- Marketing pages under (public)/, docs, legal, pricing, blog, channels, tools, and similar routes
- API GET, HEAD, and OPTIONS — including public CMS catalog routes (/company/*, /blog-system/*, /listings/*)
Operational endpoints
- /health and /sitemap.xml
- Stripe webhooks under /webhooks/ (small billing writes during a short window are acceptable; Stripe retries on 503)
Frozen surfaces
- API mutations (auth, public API writes, MCP POST, uploads, and anonymous public writes)
- Web auth and app routes — sign-in, account, editor, admin, OAuth approve, join-org, CLI device pages → /maintenance
- BullMQ workers — process exits immediately without consuming queues
Operator bypass (optional)
When MAINTENANCE_BYPASS_SECRET is set on the API, send the same value in the X-Maintenance-Bypass request header to allow a single mutation during freeze_writes (for example a smoke-test POST). Do not share the secret publicly.
Cutover timeline (region migration)
Use with Supabase backup. Typical sequence:
Enable write-freeze on all services
Set MAINTENANCE_MODE=freeze_writes on the Vercel backend and web projects and on every Railway worker service (or recreate self-host API, web, and worker containers). Redeploy.
Verify: public blog returns 200; /sign-in redirects to /maintenance; a test POST to the API returns 503 with Retry-After.
Run a fresh pre-cutover backup
With writes frozen, run Layer 2 and Layer 3 again using a -pre-cutover suffix (see Supabase backup docs).
Restore, migrate Storage, and cut over env
Follow Cutover freeze runbook. Update Supabase URLs and keys in production env only after restore and Storage migration succeed.
Smoke test and resume
Run pnpm prod-backup:smoke (see Supabase backup — smoke test), then finish the printed manual checks (Google login, scheduled post, provider OAuth). Set MAINTENANCE_MODE=off on API, web, and workers; redeploy and resume worker processes.
Order matters
Enable freeze_writes before the final Layer 2 dump — not after. Workers should not consume jobs while the database is being restored.
Example (production cutover)
Backend (backend/.env.production.local):
MAINTENANCE_MODE=freeze_writes
MAINTENANCE_RETRY_AFTER_SECONDS=3600
MAINTENANCE_BYPASS_SECRET=your-operator-secret Web server env (web/.env.production.local — sync to Vercel, no VITE_ prefix):
MAINTENANCE_MODE=freeze_writes Workers (orchestrator/.env.production.local — same values as API):
MAINTENANCE_MODE=freeze_writes
MAINTENANCE_RETRY_AFTER_SECONDS=3600
MAINTENANCE_BYPASS_SECRET=your-operator-secret Self-host Compose: set the same keys in infra/self-host/.env (API and workers load the full file; the web service receives MAINTENANCE_MODE at runtime). Recreate containers after changes.