Audience: SREs, platform operators, DevOps engineers Last updated: 2026-02-23 Infrastructure: EKS (Kubernetes), RDS PostgreSQL, ElastiCache Redis, PgBouncer
Table of Contents
- Scaling the API Layer
- Scaling Workers
- Scaling the Database
- Scaling Redis
- Scaling PgBouncer
- Capacity Planning
1. Scaling the API Layer
Current Architecture
Horizontal Pod Autoscaler (HPA)
Default HPA configuration (from Helm chart):HPA Tuning
Adjusting target CPU utilization:Manual Scaling
For planned events or emergencies:Pod Resource Tuning
Pre-scaling for Planned Events
Scheduler Behaviour When Running Multiple API Replicas
Each API pod starts two in-process schedulers on boot:- The Flow scheduler (fires due pipelines and
file_arrivaltriggers) takes a PostgreSQL advisory lock, so it is safe to run many API replicas — only the lock holder fires each tick. Disable withFLOW_INTERNAL_SCHEDULER_ENABLED=false. - The maintenance scheduler (self-invokes the
/api/**/cron/*jobs whenCRON_SECRETis set) assumes a single replica — it is not distributed-locked, so N replicas run N duplicate passes. The cron jobs are idempotent, so duplicates are harmless, but for large fleets disable it on all replicas (MAINTENANCE_SCHEDULER_ENABLED=false) and drive the cron routes from one external scheduler (EventBridge or a K8s CronJob) instead.
2. Scaling Workers
Current Architecture
KEDA Autoscaler
The Helm chart ships a KEDAScaledObject for the worker when
worker.keda.enabled: true. It scales the kaireon-worker deployment on the
depth of the BullMQ queue lists. Chart defaults: minReplicas: 1,
maxReplicas: 10, queueThreshold: "5" (see helm/values.yaml).
Rendered ScaledObject (from helm/templates/worker-deployment.yaml):
batch-jobs, dsar-jobs, journey-jobs,
retrain-jobs, and seed-jobs. Add a redis trigger per queue you want to
scale on, pointing listName at the matching bull:<queue>:wait key.
KEDA Tuning
Adjusting trigger thresholds:
Only
minReplicas, maxReplicas, and queueThreshold (→ listLength) are
exposed through helm/values.yaml; the other columns require editing the
ScaledObject template or a custom overlay.
Manual Worker Scaling
Worker Resource Tuning
Workers are CPU-intensive during scoring and memory-intensive during pipeline execution.Queue-Specific Worker Pools
For isolating workloads, deploy separate worker pools per queue:3. Scaling the Database
Vertical Scaling (RDS)
Instance type progression:
Scaling up:
Read Replicas
Use read replicas to offload read-heavy queries (dashboards, analytics, reporting). Creating a read replica:DATABASE_URL and does not expose
a read/write-split env var (there is no DATABASE_READ_URL). To serve reads
from a replica, either point read-heavy tooling (dashboards, ad-hoc analytics,
BI) directly at the replica endpoint, or front reads with a proxy/PgBouncer
that routes to the replica. Routing the application’s own reads to a replica
requires application-level changes, not a config toggle.
Monitoring replica lag:
Storage Scaling
Connection Limit Scaling
When scaling the database vertically, adjustmax_connections accordingly:
4. Scaling Redis
Vertical Scaling (ElastiCache)
Instance type progression:
Scaling up (single node, non-clustered):
Cluster Mode
For datasets that exceed single-node memory or require higher throughput. Enabling cluster mode:Redis Memory Management
Scaling Redis for Specific Use Cases
Decision caching (read-heavy):- Use read replicas for read distribution.
- Set short TTLs (30-60s) to limit memory growth.
- Use
volatile-lrueviction.
- Scale vertically for more throughput.
- Monitor
instantaneous_ops_per_sec. - Consider cluster mode if >100K ops/sec.
- Separate from cache Redis.
- Use
noevictionpolicy (sessions must not be evicted). - Size for peak concurrent users.
5. Scaling PgBouncer
Pool Size Calculations
Scaling PgBouncer Horizontally
Deploy multiple PgBouncer instances behind a Kubernetes Service:
Important: Total
max_db_connections across all PgBouncer instances must not exceed PostgreSQL max_connections.
Applying Pool Changes
6. Capacity Planning
Metrics to Track
Monthly Capacity Review Checklist
- Review 30-day trends for all metrics above.
- Project growth for the next 90 days based on customer onboarding pipeline.
- Identify any component within 30 days of hitting a threshold.
- Plan scaling actions with cost estimates.
- Update this document with new current values.
Cost-Aware Scaling
- Use Spot instances for batch workers (up to 70% savings).
- Use Reserved Instances for baseline API and database capacity.
- Use Graviton (arm64) instances for 20% better price-performance.
- Scale down non-production environments during off-hours:
Load Testing Before Scaling
Before major scaling changes, validate with load tests:- P50, P95, P99 latency at each VU level.
- Error rate at each VU level.
- Database connection count and query time.
- Redis memory and ops/sec.
- Pod CPU and memory utilization.