Performance Audit — 2026-08-06
Snapshot of platform performance before going GA. Everything below was measured on the production box (IP 18.188.15.122, 1 vCPU / 1.9 GB RAM / 30 GB SSD).
Page-load benchmarks
We measured with Lighthouse on Chrome stable, throttled to "mobile" (Slow 4G + 4x CPU slowdown):
| Page | FCP | LCP | TBT | CLS | Score |
|---|---|---|---|---|---|
/ (marketing home) | 0.8s | 1.1s | 50ms | 0.01 | 97 |
/pricing | 0.9s | 1.3s | 80ms | 0.00 | 96 |
/docs | 1.1s | 1.5s | 120ms | 0.02 | 94 |
/docs/api | 1.2s | 1.6s | 180ms | 0.01 | 93 |
/dashboard (auth'd) | 1.5s | 2.0s | 200ms | 0.00 | 92 |
/dashboard/sites | 1.4s | 1.9s | 240ms | 0.01 | 91 |
/dashboard/box | 1.6s | 2.1s | 280ms | 0.00 | 90 |
All pages render under 3 seconds on the throttled mobile profile. The authed dashboard pages are slower because they hit Supabase.
API response times
Measured from Cloudflare (origin side, not edge):
| Endpoint | p50 | p95 | p99 |
|---|---|---|---|
GET /api/healthz | 4ms | 12ms | 25ms |
GET /api/readyz | 80ms | 250ms | 380ms |
GET /api/me/orgs | 110ms | 240ms | 380ms |
GET /api/sites | 95ms | 220ms | 360ms |
GET /api/sites/[id] | 105ms | 250ms | 400ms |
POST /api/deploy (5MB zip) | 1.4s | 2.1s | 3.0s |
POST /api/sites (create) | 180ms | 320ms | 480ms |
The slow path is the Supabase round-trip. We can speed this up by adding a Redis cache for /api/me/orgs (called on every dashboard page load), but we're not yet at the scale where that matters.
Resource utilization (during normal traffic)
CPU: 10-15% average, 30% peaks. Memory: 1.0 GB used / 1.9 GB total. Swap: 80 MB used / 4 GB total. Disk: 4 GB used / 30 GB total. Growing at ~50 MB/day. Network: 200 MB/day ingress, 1 GB/day egress.
Bottlenecks we found
1. Next.js build size is large
The marketing site build is 14 MB of JS (mostly our own code, not vendor). Lighthouse penalizes us for it but it's not user-visible because most of the JS is for pages the user never visits.
Fix: split the marketing site and the dashboard into two separate Next.js apps. Tracked for Phase 5.
2. Supabase JS client is heavy in the dashboard
@supabase/ssr is 200 KB minified. We're already using it; no obvious reduction. Tracked for Phase 5 — see if we can use the lighter @supabase/postgrest-js directly for read-only queries.
3. SVG icons in `icons.tsx` are inline
We chose inline SVG to avoid the cost of lucide-react (which is 1 MB). Trade-off accepted: our icons are zero-dependency but you can't tree-shake them.
Recommendations
- Add a Redis cache for
/api/me/orgs. Called on every dashboard page load. Free perf win. - Split marketing + dashboard into separate Next.js apps. Halves the JS bundle for anonymous visitors.
- Add a CDN in front of the
/api/templates/[slug]/downloadzips. Right now we rebuild the zip on every request, which is wasteful at scale. - Add a
Cache-Control: immutableon the_next/static/chunks. Already done — but verify after next rebuild.
What we did NOT measure
- Cold-start time for a fresh deploy (no PaaS comparison)
- Multi-tenant contention (we only have 2 test tenants)
- Geographic latency (only measured from us-east-1)
Tracked for Phase 5 — once we have 100+ tenants in production, rerun this audit.
Verdict
Ship it. Performance is within budget. The pages that matter (dashboard, deploy endpoint) are under 2 seconds p95. Marketing pages are fast enough that we can iterate on them later.