Cost & Scaling
What the platform costs to run, and what has to change as it grows.
These are list-price estimates
Derived from the Terraform configuration and DigitalOcean's published pricing. Validate against the actual invoice — usage-based services (Mux, Spaces bandwidth, CDN) vary with traffic, and make cost in infra/terraform deliberately points you at the DigitalOcean calculator rather than guessing.
Fixed monthly cost
| Item | Configuration | ≈ / month |
|---|---|---|
| Kubernetes nodes | 3 × s-4vcpu-8gb | $144 |
| Kubernetes control plane | Standard | $0 |
| Managed PostgreSQL | db-s-2vcpu-4gb, 1 node | $60 |
| LiveKit droplet | s-4vcpu-8gb | $48 |
| Load Balancer | 1 | $12 |
| Container Registry | Basic tier | $5 |
| Spaces | Base, 250 GB included | $5 |
| Block storage | 25 GB (Loki 20 Gi + Redis 5 Gi) | $3 |
| Subtotal | ≈ $277 |
Usage-based
| Service | Driver | Notes |
|---|---|---|
| Mux | Minutes encoded, minutes delivered | The most variable line. Video-heavy growth shows up here first |
| Spaces bandwidth | CDN egress beyond the included allowance | Grows with media consumption |
| Node autoscaling | Load | Pool scales 2–10; each extra node is $48 |
| RevenueCat | Transaction volume | Free below a revenue threshold, then a percentage |
| Stripe | Payout volume | Per-transfer fees |
| Bugsnag | Events | |
| Expo / EAS | Build minutes |
Realistically $300–450/month all-in at modest usage, with Mux the line most likely to surprise.
Cost levers
| Lever | Saving | Trade-off |
|---|---|---|
| Drop to 2 nodes | −$48 | Less headroom; autoscaling still covers bursts |
| Smaller LiveKit droplet | −$24 | Fewer concurrent voice participants |
| Self-hosted Postgres in-cluster | −$60 | You own backups, failover, and upgrades. Not recommended |
| Spaces lifecycle rules on originals | Storage | Originals are currently kept forever after processing |
| Loki retention policy | Storage | Currently unbounded until the 20 Gi fills |
A single-droplet alternative exists on paper
dev-notes/single-droplet-feasibility-analysis.md concludes the whole stack fits on one 4 GB / 2 vCPU droplet (~$24/month plus external services) for 200–500 concurrent users.
That is a genuine option for a much earlier stage, but it trades away everything Kubernetes is providing here: zero-downtime rollouts, replica-level redundancy, autoscaling, and managed database backups. Given the platform is live and handling real money, the current setup is the right call. Note also that the analysis's comparison figure ($148–153/month) reflects a smaller cluster than the one currently configured.
What breaks first as you grow
In the order it will actually happen:
1. Database connections
db-s-2vcpu-4gb has a bounded connection limit. Three API replicas plus esu, ogun, anansi, and igdb-heartbeat each hold a Prisma pool. Adding API replicas multiplies connections, so the database limit caps horizontal scaling of the API before CPU does.
Fix: a connection pooler (PgBouncer, or DigitalOcean's managed pooler). Cheap, and it should be done before scaling the API.
2. Redis as a single point of failure
One replica backing the queue, realtime fan-out, caching, and the payout lock. A restart interrupts all four.
Fix: DigitalOcean Managed Redis with HA. Roughly $15–60/month depending on size.
3. redis.keys() on every post
Feed cache invalidation calls KEYS, which is O(N) over the entire keyspace and blocks the Redis server while it runs. At current volume it is invisible; as the keyspace grows it becomes a latency spike felt by every service sharing that Redis.
Fix: SCAN, or track each user's cache keys in a Redis set. Contained change, no infrastructure cost.
4. The discover feed
prisma.user.findMany() with no limit — it returns every user on the platform. It will get slower in proportion to signups and eventually time out.
Fix: proper ranking, filtering, and pagination. See Discovery.
5. Presence across esu replicas
Presence is an in-memory map per pod, so online status is already inconsistent across the two replicas. It gets worse with every replica added.
Fix: move presence into Redis.
6. Media processing throughput
ogun autoscales 2–8 on CPU, which handles bursts. The next ceiling is Mux's rate limits on asset creation.
7. Mux costs
Encoding and delivery both scale with video volume. Watch this line specifically as clip usage grows; video_quality: "basic" is already the economical setting.
Scaling actions in order
| Signal | Action | Cost |
|---|---|---|
| API CPU sustained high | Add API replicas — but check database connections first | $0 until a node is added |
| Postgres connection errors | Add a connection pooler | ~$15 |
| Nodes at capacity | The pool autoscales to 10 | $48 per node |
| Redis latency or restarts | Move to Managed Redis with HA | $15–60 |
| Database CPU or storage pressure | Resize the managed cluster | Step up in tiers |
| Media backlog growing | ogun HPA handles it; raise maxReplicas if needed | Node cost only |
| Voice quality degrading | Larger LiveKit droplet, or a second one | $24–48 |
Things worth spending money on
Not all of these are infrastructure, and the non-infrastructure ones are the better investment:
| Investment | Cost | Returns |
|---|---|---|
| Staging environment | $30–50/month | Stops untested changes reaching production. Highest value per dollar here |
| Managed Redis | $15–60/month | Removes a single point of failure |
| Connection pooler | ~$15/month | Unblocks API scaling |
| CI/CD | $0 (GitHub Actions free tier) | Automated verification and traceable deploys |
| Alerting | $0 (Grafana already deployed) | Find outages before users report them |
| Uptime monitoring | $0–10/month | Catches the case where the cluster is down and therefore not logging |
Three of the six cost nothing and are limited only by engineering time.
Efficiency already in place
Worth noting, because it is the reason costs are as low as they are:
- Aggressive image caching (
max-age=31536000, immutable) — the CDN serves almost everything - Redis caching on the feed (60 s) and trending (5 min) — the expensive queries run rarely
- Presigned direct-to-Spaces uploads — media bytes never consume API bandwidth or CPU
video_quality: "basic"on Mux — the cheaper encoding tierwithoutEnlargementon image resizing — no wasted work upscaling small originals- Node autoscaling from 2 — the cluster scales down when idle
