Module 13 / 20 · Phase C — Scale & Reliability · 40 min

Statelessness
& sessions.

We've been quietly saying "stateless" for modules. Now's the moment to look closely. Where does the user's identity actually live? How do you remember someone across requests when each one might hit a different server? This is the discipline that unlocks the whole pattern.

// What you'll know by the end

  • What "state" really is in a server
  • Why in-memory sessions break horizontal scale
  • The three common solutions and their failure modes
  • JWT vs server-side sessions — when to use which
§ 01 — A small mystery

"Why am I
logged out?"

You build a login system. Alice signs in, your server creates a session, stores her info in memory, and gives her a session ID cookie. She makes a few more requests. Then she comes back later and gets "please log in again." What happened? Your code didn't change. Your servers are all running. The session cookie is right there in her browser. So why is she logged out?

// 4 REQUESTS · 3 SERVERS · ROUND-ROBINED BY LOAD BALANCER
Request 1: POST /login SERVER A ✓ session created on A
Request 2: GET /profile SERVER B ✗ B has no session for Alice
Request 3: POST /settings SERVER C ✗ C has no session either
Request 4: GET /dashboard SERVER A ✓ A still has session

Three out of four requests fail. Not because the code is wrong — the code on Server A worked perfectly. The problem is that the session lives only on Server A, and the load balancer is happily distributing Alice's requests across all three. To Server B, Alice looks like a stranger. To Server C, the same. This is the bug that bites every team the first time they go from 1 server to 2 — and the lesson it teaches is the most important pattern in horizontal scaling: statelessness.

§ 02 — What "state" really is

If it disappears
when the server dies.

State is anything a server remembers between requests. Variables in memory. Files on its local disk. Open database connections. Cached computations. Any of these create a dependency: "future requests must come back to this server because only it has this thing." And that dependency is what breaks horizontal scaling. If even one piece of important data lives only on one box, your "scalable" cluster turns into a fragile single-server system pretending otherwise.

// WHICH OF THESE ARE STATE? (HINT: MOST OF THEM)

Static config// env vars, constants
Set once at startup, identical across all servers. Not state in the harmful sense — every server has the same value. Safe.
NOT STATE
User session// "Alice is logged in"
Created at login, lives in memory on whichever server handled the login. Pure state. The exact problem we just saw. Must be moved out of process memory.
STATE — BAD
In-process cache// this.cachedThings
Looks innocent. But now Server A has a warm cache and Server B has nothing. Inconsistent behavior, harder to scale. Use Redis instead of a local Map.
STATE — BAD
Uploaded files// images, attachments
If you save uploads to the local disk, the file exists on one server only. The next request might go elsewhere and find nothing. Use object storage (S3) instead.
STATE — BAD
Rate-limit counter// "this IP made 100 reqs"
If each server counts independently, an attacker hitting 5 servers can do 5× the limit. Counters need to live somewhere shared — Redis is the canonical choice.
STATE — MIXED
Database connection pool// open TCP connections
Yes, technically state — but it's per-process infrastructure rather than user data. Every server has its own pool to the shared DB. Fine, this is how it should work.
NOT STATE

The pattern is now visible: state about a particular user, request, or action is the dangerous kind. Move it out of the server process and into a place every server can read — a database, a Redis cache, an object store. Once your servers don't remember anything specific about who's talking to them, you can spin up 5 more in 30 seconds and they all behave identically.

A stateless server treats every request like a stranger. Which, for the load balancer, is exactly what you want.

This is also the heart of the "12-factor app" principles that nearly every modern cloud platform expects: stateless processes, state in backing services. Why? Because it's what makes everything else — load balancing, auto-scaling, rolling deploys, failover, blue/green — actually work without users noticing.

§ 03 — Three ways to handle sessions

Three answers,
very different futures.

When the question "where does the user's session live?" comes up, the industry has settled on three patterns. They're worth knowing because you'll see all three in real systems — sometimes in the same system. One is a beginner's mistake. One works but is fragile. One is the modern default. The next section turns this into a live comparison; first, a quick tour.

// OPTION 1

In-memory sessions

"the beginner's mistake"

Store sessions in a JavaScript object or a server-side framework map. Works perfectly with one server. Breaks immediately when you go to two, because each session lives on whichever server happened to handle the login. The trap we opened the module with.

// OPTION 2

Sticky sessions

"the duct tape fix"

Configure the load balancer to "pin" each user to the same server. Now Alice always lands on Server A. The session-in-memory problem disappears — until Server A dies, and Alice's session dies with it. Works, but inherits the SPOF of vertical scaling.

// OPTION 3

External store / JWT

"the modern default"

Move sessions out of the server. Two flavors: store sessions in Redis and have every server read from it (server-side sessions), or encode the user's identity into a signed JWT token that any server can verify without lookup. Stateless servers, full horizontal scale.

The lab below puts all three side-by-side, running the same scenario. You'll see exactly where each one breaks and which one survives a server failure. Then we'll dig into the JWT-vs-Redis trade-off in §05.

§ 04 — Three strategies, one scenario · interactive lab

Same requests.
Three fates.

Three columns, three strategies. Same scenario in each: a user logs in and makes more requests. The load balancer round-robins across servers (except in sticky mode). Hit Send next request repeatedly to walk through. Then hit Kill S1 and watch one of the three fall apart while another doesn't notice.

SESSION_STRATEGIES.SIM // m.13 lab
// OPTION 1 · IN-MEMORY
Sessions in RAM
round-robin · no coordination
Alice LB S1 (empty) S2 (empty) S3 (empty) sessions live in each server's RAM other servers don't see them
Successes0
Failures0
idle · press Send
// OPTION 2 · STICKY
Pin to one server
LB cookie · always lands on S1
Alice 🔒 LB S1 (empty) S2 (unused) S3 (unused) LB pins Alice to S1 via cookie / IP hash
Successes0
Failures0
idle · press Send
// OPTION 3 · EXTERNAL / JWT
Session in Redis
all servers read shared store
Alice LB S1 stateless S2 stateless S3 stateless REDIS (empty) all servers read from Redis any server can serve any req
Successes0
Failures0
idle · press Send
// THE VERDICT
Press Send to fire the first request

All three strategies will handle login (Request 1) successfully. The differences appear at Request 2 — when in-memory sessions discover their other-server problem. Then hit Kill S1 to see which strategy survives losing a server.

§ 05 — Redis sessions vs JWT

Two flavors of
doing it right.

Option 3 — externalizing session state — has two distinct sub-patterns. Both work; both are modern; they trade off differently. Server-side sessions in Redis: server gives the client an opaque session ID, and looks up the real data in Redis on every request. JWT tokens: the server signs a token containing the user's identity, hands it to the client, and the client sends it back on every request — no lookup needed, the token itself is the proof.

// SERVER-SIDE SESSIONS vs JWT

Property
Redis Sessions
JWT Tokens
Where data lives
In Redis (or another shared store). Client holds a small opaque ID like sess_a7b2f9.
In the token itself. Client holds the entire signed payload: user ID, role, expiry — everything inline.
Per-request cost
Redis lookup on every request (~1ms in same data center). Adds a hop.
Just verifying the signature locally. No lookup at all. Slightly cheaper per request.
Can you revoke?
Yes — delete the row in Redis and the user is logged out instantly. Easy.
Hard. The token is valid until it expires; you'd need a separate "revocation list" lookup, which kind of defeats the point.
Token size
Tiny — just an opaque ID, ~30 bytes.
Larger — typically 300-1000 bytes since it contains real claims. Sent on every request.
Best for
Web sessions where you control both client and server, and need clean logout / revocation.
Microservices, APIs, mobile apps — anywhere downstream services need to verify identity without phoning home.

The pragmatic rule of thumb: use Redis-backed sessions for normal user-facing web apps (you'll want revocation, and the lookup is cheap), and use JWTs for service-to-service auth, mobile APIs, and microservices (where avoiding a per-request lookup actually matters and revocation matters less). Many systems use both — JWT for API calls, Redis sessions for browser cookies.

One more thing worth mentioning: JWTs are signed, not encrypted. The contents are readable by anyone — they're just base64-encoded JSON. Don't put secrets in a JWT. The signature only guarantees "this was issued by me and hasn't been tampered with." If you need encryption, there's JWE for that — but most production systems just keep claims minimal and avoid the issue.

§ 06 — Eight words for the session layer

Vocabulary,
for the stateless life.

You'll see these in every architecture review involving login. Get fluent.

Stateless
/ˈsteɪtləs/
A server that remembers nothing between requests. Every request carries the full context it needs. The prerequisite for horizontal scaling.
Session
/ˈsɛʃən/
The server-side concept of "this particular user is logged in right now." Lives somewhere — in memory, Redis, or implicit in a token.
Cookie
/ˈkʊki/
A small piece of data the browser stores and re-sends with every request to a given domain. Most session IDs travel as cookies.
JWT
/ˈdʒɒt/ ("jot")
"JSON Web Token." A signed token containing claims (user ID, role, expiry). The server verifies the signature; no lookup needed. Stateless auth.
Sticky Sessions
/ˈstɪki/
A load-balancer mode where each user is consistently routed to the same backend server. Workaround for in-memory state; loses the session if that server dies.
Session Store
/ˈsɛʃən stɔː/
An external storage system (typically Redis) that holds session data. All servers read/write to it. Allows servers to remain stateless without losing the user.
Revocation
/ˌrɛvəˈkeɪʃən/
Invalidating a session or token mid-life (logout, security incident, password change). Easy with stored sessions; tricky with JWTs.
12-factor App
/twɛlv ˈfæktə/
A set of principles for building cloud-native apps. Statelessness is one of them: "store state in backing services, not the process." Foundational reading.
§ 07 — Knowledge check

Five questions.
Keep your state out.

Test the statelessness intuition. Click an answer; the explanation arrives instantly.

QUESTION 1 OF 5
Loading question...
Score: 0 / 5
5 / 5

Stateless.

You see the failure modes. Now onto Auth itself — how the identity in those tokens actually gets established.

§ 08 — The recap

Three ideas to
carry forward.

This is one of those modules whose ideas show up in nearly every system you'll ever design.

i

State on the server kills scale

Any data tied to one specific server box is a constraint that breaks horizontal scaling. Move user-related state out of process memory.

ii

Three patterns, one winner

In-memory: don't. Sticky: works but fragile. External store or JWT: the modern default. Pick external for revocation; JWT for service-to-service.

iii

Stateless servers survive everything

Failures, rolling deploys, autoscaling — none of them feel scary when servers carry no per-user memory. The discipline pays back constantly.

↓ UP NEXT

M.14 — Auth 101:
OAuth & JWT.

We've talked about tokens and session IDs as if they appear by magic. Time to look at where authentication actually happens — the OAuth dance, JWT structure, and how "sign in with Google" really works under the hood.

Continue to Module 14 →