Scale-up vs scale-out, stateless services, and when each approach wins.
Published September 21, 2026
When a system can't keep up (pages slow down, CPU sits at 100%, the database queue grows), you have two basic ways to add capacity:
Real systems use both, often for different components. The skill is knowing which applies where, and what each one requires of your architecture.
Move from 4 vCPUs and 16 GB of RAM to 64 vCPUs and 512 GB. Nothing about the software changes.
Advantages:
Limits:
It's worth stating plainly: vertical scaling goes much further than many people assume. A single modern database server with plenty of RAM and fast NVMe storage handles very large workloads. Scaling up first is often the correct, cheapest decision, especially for databases.
Run N copies of the service behind a load balancer:
ββββΆ app-1 ββ
clients ββΆ LB βββΌβββΆ app-2 ββΌβββΆ shared state: database Β· cache Β· object storage
ββββΆ app-3 ββ
Advantages:
Requirements and costs:
A service is stateless if any instance can handle any request, because nothing needed for later requests is kept only in one instance's memory or local disk.
β Session stored in app-1's memory β the next request lands on app-2 β the user is "logged out"
β An uploaded file saved to app-1's local disk β app-2 can't serve it
β An in-memory rate-limit counter per instance β the real limit becomes N Γ the intended one
β
Sessions in Redis, or self-contained signed tokens (JWT)
β
Files in object storage (S3 / GCS / Azure Blob)
β
Counters and locks in a shared store
β
Background jobs in a queue that any worker can consume
Sticky sessions (the load balancer always routes a user to the same instance) are a workaround, not a fix. Load becomes uneven, and users lose their state when their instance dies or is replaced during a deployment.
| Tier | Usual approach |
|---|---|
| Web / API servers | Horizontal: make them stateless, add instances, autoscale |
| Background workers | Horizontal: more consumers on a queue |
| Cache | Scale up first, then shard across nodes (Redis Cluster) |
| Relational database | Vertical first, then read replicas for reads, then caching, and finally sharding for writes if truly needed |
| Object storage / CDN | Managed services that scale out for you |
The database order matters. Each step adds complexity:
Take each step only when the previous one runs out.
Cloud platforms add and remove instances automatically, based on metrics:
apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata: { name: api }
spec:
scaleTargetRef: { apiVersion: apps/v1, kind: Deployment, name: api }
minReplicas: 3 # enough for redundancy even when idle
maxReplicas: 30 # a ceiling protects the database and the budget
metrics:
- type: Resource
resource: { name: cpu, target: { type: Utilization, averageUtilization: 65 } }
Practical points:
maxReplicas limit prevents a traffic spike, or a bug, from overwhelming the database behind the app tier.Q: Why is horizontal scaling usually preferred in system design? A: It has no fixed ceiling, provides fault tolerance through redundancy, and supports elastic, rolling operation. But it requires stateless services, and it pushes the hard problems down to the shared data tier. Vertical scaling is still often right for databases.
Q: What makes a service stateless, and how do you handle sessions? A: No instance keeps data that later requests depend on. Sessions go into a shared store such as Redis, or become signed tokens the client presents on each request. Files go to object storage, and shared counters to a central store.
Q: How do you scale a database that has hit its limits? A: In order: optimize queries and indexes, scale up the hardware, add caching, add read replicas for read-heavy load, then partition data by function or shard by key for write-heavy load. Each step adds consistency and operational complexity.
Q: Why are sticky sessions discouraged? A: They tie a user's state to one instance, which unbalances load, loses sessions when the instance fails or is redeployed, and hides the underlying statefulness problem.
Q: What metrics would you autoscale on? A: Whatever best tracks saturation for that service: CPU for compute-bound APIs, queue depth or message lag for workers, requests per second or concurrency per instance for I/O-bound services. Combine it with a minimum for redundancy and a maximum to protect downstream systems.