Skip to content

Scaling Considerations

Guidance for scaling MediaLocker beyond a single-node deployment.

When to Scale

MetricSingle Node LimitAction
Total objectsStorage scales with Hetzner Object Storage, not local drives
Total storageStorage scales with Hetzner Object Storage, not local drives
API requests500 req/sScale API horizontally
Concurrent uploads100+Scale API (object bytes go browser→Hetzner via presigned URLs)
Organization count5,000+Move to a larger Supabase Cloud compute tier

Object Storage

Object storage is managed by Hetzner Object Storage, so there is nothing to scale at the deployment level — capacity and durability are handled by the provider. Uploads and downloads go directly between the client and Hetzner via presigned URLs, so they never flow through your compute nodes. Run the worker in the same Hetzner region as the bucket data to keep derivative generation fast.

API Horizontal Scaling

Scale the API service behind Caddy load balancing:

yaml
api-1:
  build:
    context: .
    dockerfile: apps/api/Dockerfile

api-2:
  build:
    context: .
    dockerfile: apps/api/Dockerfile

# Caddy load balancing:
api.medialocker.io {
    reverse_proxy api-1:3001 api-2:3001 {
        lb_policy round_robin
    }
}

Postgres Scaling (Supabase Cloud)

Postgres is managed by Supabase Cloud, so there is nothing to tune on the host — no shared_buffers, no self-hosted PgBouncer. Scale the database by:

  1. Upgrading the Supabase Cloud plan / compute tier to add CPU, RAM, and IOPS.
  2. Adding read replicas through the Supabase dashboard for read-heavy workloads (on supported plans).

Mind the pooler connection budget

The app already connects through the Supabase transaction pooler (port 6543). Each of api, worker, and mcp opens its own pool, and every additional replica multiplies that. The pooler enforces a connection limit tied to your compute tier — keep the sum of all api + worker + mcp pool sizes under that budget as you scale horizontally, and raise the compute tier (which raises the connection limit) before you run out.

Redis Scaling

Redis Sentinel

For high availability:

  • 3+ Redis nodes (1 primary, 2+ replicas)
  • Sentinel monitors and handles failover

Redis Cluster

For horizontal scaling of cache:

  • Multiple shards across nodes
  • Automatic data partitioning

Worker Scaling

Scale background workers by running multiple instances:

yaml
worker:
  deploy:
    replicas: 3  # Process more concurrent jobs

Each worker polls the same Redis queues — jobs are processed once.

Caching Strategy

LayerTechnologyTTL
API responsesRedis60s
Media metadataRedis300s
Presigned URLsRedisURL expiry

Monitoring at Scale

Recommended monitoring stack for scaled deployments:

MetricToolDashboard
System metricsPrometheus + Node ExporterGrafana
Application metricsOTEL → PrometheusGrafana
LogsLoki + PromtailGrafana
PostgresSupabase Cloud observabilitySupabase dashboard
Redisredis_exporterGrafana

Load Testing

Before scaling, load test to identify bottlenecks:

bash
# Using k6 for load testing
k6 run --vus 100 --duration 5m load-test.js

Key metrics to watch:

  • API latency (p50, p95, p99)
  • Error rate
  • CPU/memory utilization
  • Database connection count
  • Redis hit rate

Released under the AGPL-3.0 License.