Symptoms
- Sentry: spike
error_type: NeonConnectionErrorORPostgresError - API endpoints return 500 with
code: "internal"and message mentioning DB /readyendpoint reportsdegraded: truewith hard dep "postgres"- Grafana:
pg_up == 0OR Postgres connection latency > 5s
Severity & escalation
- PAGE 24/7 — writes fully disabled. Read fallback (snapshots in KV) lives 60s after last refresh, then goes stale
- Ack window: 15 min
- Escalate after 30 min → engineering lead
- On Neon-side outage without ETA → consider PITR restore to secondary region (Phase 16+ post-MVP)
Immediate actions (< 5 min)
- Reproduce: wrangler tail should show a DB errors stream
cd apps/api && npx wrangler tail --config wrangler.toml --format=pretty - Check Neon status: https://neonstatus.com (opens in a new tab)
- Open Neon dashboard: https://console.neon.tech (opens in a new tab) → project
arno-prod(Frankfurt)- If it shows Suspended (free tier auto-suspend) → click to open, wake up. Resume ~10s
- If it shows Error / Down → wait OR contact Neon support
- Verify connection from CLI:
psql 'postgresql://neondb_owner:***@ep-dry-block-al36bkvg.c-3.eu-central-1.aws.neon.tech/neondb?sslmode=require' -c 'SELECT 1'
Diagnosis (5-20 min)
Branch A: Neon suspended (free tier)
- This is expected behaviour on idle period > 5 min for free
- Mitigation: upgrade to Neon Launch ($19/mo) when DAU > 50 OR business critical (per cost ladder)
- Open project in dashboard → resume → verify
/readyis green
Branch B: Neon up but Workers can't connect
- Check
DATABASE_URLwrangler secret format — must bepostgresql://user:pass@host/db?sslmode=require - Check Neon allowlist (IP) — Workers exit IPs are unstable, so Neon must accept any IP (default for serverless tier)
- Check rate limit — Neon free 100 concurrent connections, we use the Drizzle HTTP driver (stateless, no pool)
- Run direct query via
tools/migrate/(Pool driver) → if it works → HTTP driver issue (unlikely)
Branch C: Neon-side outage
- Status page shows incident → wait
- Inform users via status page if > 5 min downtime
- Consider PITR restore: Neon → branch → PITR to point before outage
Recovery
| Issue | Action |
|---|---|
| Suspended (free tier) | Resume in dashboard; wake takes ~10s |
| Bad DATABASE_URL | wrangler secret put DATABASE_URL --config wrangler.toml < new-url.txt |
| Region outage | Wait on Neon, OR — emergency — create a branch in another region + repoint DATABASE_URL (PITR-based) |
| Rate-limit | Investigate runaway queries (Sentry traces) → kill stuck connections via Neon dashboard |
Verification
/readyendpoint returnsdegraded: false- API endpoints with write (
POST /api/v1/projects) succeed - Sentry error rate returns to baseline ≤5 min after recovery
- Grafana
pg_up == 1persistent for 5 min
Aftermath
- Post-mortem trigger: downtime ≥ 10 min
- Document: timeline, root cause, was PITR needed?
- If free tier suspend happens often (>1/week) → upgrade to Launch
Known false positives
- PITR backups window (nightly ~03:00 UTC) — short read latency spike, not unreachable. Do not PAGE
- Test queries from ARNO CI — may briefly increase connection count; do not PAGE