Horizontal scaling only works if instances are interchangeable. That means no important data lives inside one instance.
flowchart LR U["User"] --> LB["Load balancer"] LB --> A["api pod 1"] LB --> B["api pod 2"] A --> R["Redis: sessions"] B --> R A --> DB["Database"] B --> DB %% step U: The user sends a session cookie or token with every request. %% step LB: The balancer can send each request to any pod. %% step A: Pods hold no session in memory; they look it up per request. %% step R: Sessions live in a shared, fast store with a time-to-live. %% step DB: Durable data lives in the database; nothing important lives in a pod.
What counts as state
| State | Stateful (bad for scale-out) | Stateless alternative |
|---|---|---|
| Login session | In-memory session map | Redis/Memcached, or a signed JWT |
| Shopping cart | Server memory | Redis or database |
| Uploaded files | Local disk of the pod | Object storage (S3, GCS, Blob) |
| Scheduled jobs | Cron inside every instance | One scheduler or a queue |
| Caches | Local cache only | Shared cache, or accept short inconsistency |
Options for sessions
| Option | How | Trade-off |
|---|---|---|
| Sticky sessions | LB pins a user to one instance (cookie) | Easy, but losing that instance logs users out; uneven load; blocks scale-in |
| Shared session store | Session ID in cookie, data in Redis | Extra hop (~1 ms); Redis must be highly available |
| Client-side token (JWT) | Signed token holds user ID and claims | No lookup; hard to revoke early, keep it small and short-lived |
Why it matters for operations
Stateless pods can be killed, replaced and rescheduled at any time: rolling deploys, autoscaling, spot instances and node maintenance all assume it. In Kubernetes, use a Deployment for stateless services and a StatefulSet only for things that really own data (databases, queues).
The twelve-factor rule
"Processes are stateless and share-nothing; any data that must persist is stored in a backing service." It is the simplest test for a cloud-ready service.
Traps: enabling sticky sessions to hide in-memory state; storing uploads on a pod's disk; JWTs with long lifetimes and no revocation; a single Redis without replication holding every session.