Scaling a Self-Hosted Billing Monitor from 1 to 100 Tenants
When I built BillingWatch, I designed it for a single Stripe account. Then people started asking about multi-tenant support. Here's what the architecture looks like when you scale from 1 to 100 ten...

Source: DEV Community
When I built BillingWatch, I designed it for a single Stripe account. Then people started asking about multi-tenant support. Here's what the architecture looks like when you scale from 1 to 100 tenants. The Core Multi-Tenancy Pattern The simplest approach: row-level tenant isolation with a tenant_id on every table. BillingWatch uses FastAPI + SQLite (upgradeable to Postgres), with every query scoped to the requesting tenant: from fastapi import FastAPI, Header, HTTPException from sqlalchemy.orm import Session app = FastAPI() def get_tenant_id(x_tenant_id: str = Header(...)): if not x_tenant_id: raise HTTPException(status_code=401, detail="Missing tenant header") return x_tenant_id @app.get("/anomalies") def list_anomalies(tenant_id: str = Depends(get_tenant_id), db: Session = Depends(get_db)): return db.query(Anomaly).filter(Anomaly.tenant_id == tenant_id).all() Every webhook endpoint, every query, every dashboard call is scoped this way. It's boring and it works. Webhook Routing at Sc