Before a system grows into more instances or more services, one property decides whether it can grow at all: where its state lives. State is everything the application remembers โ database rows, session tokens, uploaded files. This chapter explains stateless services, and why you have quietly been building one all along.
A service is stateless when the running process itself holds nothing that cannot be lost. All the state lives in shared places outside the process.
The test is simple: kill the container and start a fresh one. Did any user lose anything? If not, the service is stateless.
Look at the choices you have already made in this track:
<aside> ๐ก
This is why your Week 11 and 12 deployments could simply replace the running container with a new one: nobody was logged out and no file was lost, because the container itself held nothing worth keeping.
</aside>
When one instance is not enough, there are two directions to grow. Vertical scaling means a bigger machine โ easy, but it has a ceiling and gets expensive. Horizontal scaling means running more identical copies (instances) of the same service behind one address, with a load balancer spreading the requests across them (you met load balancers briefly in the Core Program's cloud introduction).
flowchart LR
Browser ==> LB["Load balancer<br/>one address"]
LB ==> A["instance 1"]
LB ==> B["instance 2"]
A ==> DB[(PostgreSQL)]
B ==> DB
A ==> S3[["Object storage"]]
B ==> S3
style LB fill:#e8f3ff,stroke:#2f80ed,color:#123
style A fill:#eaf8ed,stroke:#2f9e44,color:#123
style B fill:#eaf8ed,stroke:#2f9e44,color:#123
This only works because any instance can serve any request โ no instance "owns" a user. In other words: horizontal scaling requires stateless services.
Imagine sessions were kept in each instance's memory instead of the database. A user logs in on instance 1; their next request lands on instance 2 โ which has never heard of them. "Randomly logged out" bugs everywhere.
The workaround is session affinity (also called sticky sessions): the load balancer pins each user to one instance. It works, but it is fragile โ if that instance dies, all its users are logged out, and load spreads unevenly. The better fix is the one you already use: keep sessions in shared storage.
๐ฌ A developer stores logged-in users in a HashMap inside the service. It works perfectly on their laptop. What breaks when the service runs as two instances?
| Kind of state | Where it belongs | Why |
|---|---|---|
| Business data (users, orders) | Relational database | Durable, consistent, transactional |
| Files (images, PDFs) | Object storage | Cheap, scalable, served by URL |
| Sessions and other short-lived data | Database table or a shared cache | Must be visible to every instance |
<aside> ๐ก
A cache is a copy of data kept close by for speed โ usually an in-memory key-value store such as Redis, which you met in Week 9. Caches make reads fast; the hard part is invalidation, knowing when the copy has gone stale. Awareness is enough for now: teams add caching when measurements show it is needed, not by default.
</aside>
A stateless service keeps its state in shared places โ database, object storage โ which makes every instance disposable. That is what makes deployments safe and horizontal scaling possible, and you have been following the pattern since Weeks 9 and 10 without calling it that. Next up: what happens to work that is waiting to be done โ messaging.
The HackYourFuture curriculum is licensed underย CC BY-NC-SA 4.0 *https://hackyourfuture.net/*

Built with โค๏ธ by the HackYourFuture community ยท Thank you, contributors
Found a mistake or have a suggestion? Let us know in the feedback form.