Stage 4 · Provision
Scalability & Capacity
Stateless Service Design
External sessions, shared caches, sticky routing risks, and immutable application instances.
Why Stateless?
Stateless services can handle any request on any instance. There is no local state that ties a request to a specific server. This enables horizontal scaling, zero-downtime deployments, and easy failover. If an instance dies, another instance handles the request without data loss.
Externalizing State
Move all state out of the application process into external stores: Redis for sessions, PostgreSQL for data, S3 for files. The application becomes a stateless processor that reads from and writes to external stores.
Stateless App Instance 1 ──┐
Stateless App Instance 2 ──┼──► Redis (sessions)
Stateless App Instance 3 ──┘──► PostgreSQL (data)
└──► S3 (files)
Any instance can handle any request.
State lives outside the application.All state is externalized. The application instances are interchangeable. Scaling is as simple as adding more instances behind a load balancer.
Sticky Routing Risks
Sticky routing (session affinity) pins a user to a specific instance. This creates implicit state — the user's session data only exists on that instance. If the instance dies, the user's session is lost. Sticky routing also prevents even load distribution.
- Uneven load — Popular users attract more traffic to specific instances.
- Deployment issues — Cannot drain an instance without dropping sessions.
- Failover risk — Instance failure loses all pinned sessions.
- Scaling problems — Cannot scale down without disrupting sessions.
Session Storage Patterns
| Pattern | Storage | Latency | Durability |
|---|---|---|---|
| In-memory | App instance | Nanoseconds | Lost on crash |
| Distributed cache | Redis cluster | Sub-millisecond | High |
| Database | PostgreSQL | Milliseconds | Very high |
| Cookie/JWT | Client-side | None (client sends) | Encrypted |
import jwt
from datetime import datetime, timedelta
SECRET_KEY = "your-secret-key"
def create_session(user_id: str, roles: list) -> str:
payload = {
"user_id": user_id,
"roles": roles,
"exp": datetime.utcnow() + timedelta(hours=1),
"iat": datetime.utcnow()
}
return jwt.encode(payload, SECRET_KEY, algorithm="HS256")
def validate_session(token: str) -> dict:
try:
return jwt.decode(token, SECRET_KEY, algorithms=["HS256"])
except jwt.ExpiredSignatureError:
raise Unauthorized("Session expired")JWTs store session data in the token itself. The server does not store any state. The tradeoff: JWTs cannot be invalidated before expiry without additional infrastructure.
Immutable Instances
Immutable instances are never modified after deployment. Instead of patching running instances, deploy a new instance and terminate the old one. This ensures every instance is identical and eliminates configuration drift.
Stateless Design Checklist
- No local file state — All files in S3 or shared storage.
- No in-memory sessions — Sessions in Redis or JWT.
- No local cache — Use Redis or Memcached.
- No instance-specific config — All config from environment variables.
- No sticky sessions — Load balancer distributes freely.
Stateless design enables horizontal scaling, zero-downtime deployments, auto-scaling, and easy failover. It is the foundation of modern cloud-native architecture. Always design stateless first.
Mark this lesson complete to store local progress and unlock a cleaner resume path the next time you visit.