When I practice system design, the part that has always felt a little unsatisfying is that the diagram usually gets the final say.
You can draw a load balancer, put PostgreSQL behind your application servers, add a private subnet, and end up with something that looks perfectly reasonable.
But the diagram itself never proves that PostgreSQL is actually private.
That is the idea I kept coming back to while building Torollo:
What if the diagram was only the intended architecture, and the running system was the thing that decided whether you were right?
That became the core of the project.
The architecture actually runs
Torollo is a local system-design lab.
You build an architecture visually, but the nodes on the canvas are backed by real Docker resources on your machine.
If you add a server, there is a container behind it.
If an exercise uses PostgreSQL, PostgreSQL is actually running.
If the architecture introduces network boundaries, Torollo has real Docker networking underneath them.
The roadmap then validates the environment that exists instead of checking whether your diagram looks correct.
One of the free roadmaps, for example, has you build a resilient three-tier application.
You start with a web server and a database, then progressively introduce:
- a load balancer
- multiple web servers
- network restrictions
- failure scenarios
At one point, PostgreSQL is supposed to be isolated from the load balancer.
Torollo actually checks that.
If the load balancer can still reach PostgreSQL on port 5432, the step fails.
You change the configuration, run the validator again, and the step only turns green when that connection is really blocked.
That loop ended up becoming the part of Torollo I care about the most:
Build something → make an assumption → let the runtime prove whether the assumption is true.
The canvas is only one layer
The frontend is built with:
- React
- TypeScript
- React Flow
The backend uses:
- Node.js
- Express
- TypeScript
- Dockerode
But I try to keep a strict separation between what the canvas says exists and what Torollo can actually observe.
If the frontend says a server exists, that is not enough for a validator.
The validator can inspect whether the container is actually running.
If an architecture says an application can reach its database, Torollo can test that connection.
If a roadmap asks you to create a table, the validator can inspect PostgreSQL.
If the exercise uses Redis, it can inspect actual Redis state.
That was an important design decision for me, because otherwise Torollo would just become another graph editor with a green checkmark attached to it.
The runtime has to be able to disagree with the diagram.
Docker networking made this much harder
The container lifecycle was relatively straightforward.
Networking was not.
Some Torollo labs use Docker bridge networks, routing, NAT and iptables rules to make the architecture behave differently depending on how you configure it.
That is where I started running into the fact that "runs on Docker" does not mean every Docker environment behaves identically.
Rootless Docker is a good example.
It can run the containers, but some of the advanced networking behavior Torollo uses requires capabilities that a rootless daemon cannot provide in the same way.
Initially, I wanted to hide details like that from the learner.
I eventually decided that was the wrong abstraction.
Torollo is specifically trying to make infrastructure behavior tangible. Pretending that the underlying environment does not matter would work against that.
So the CLI now includes:
npx torollo doctor
It checks the Docker environment, ports and some runtime requirements and tries to explain what is wrong before you spend time debugging an exercise that your machine cannot execute properly.
That feature came directly from repeatedly debugging what looked like Torollo failures and discovering that the actual problem was the environment.
I’m not trying to recreate AWS locally
This is probably the easiest trap for a project like Torollo to fall into.
Once you have containers, databases, load balancing, queues, caching and networking, there is always another infrastructure primitive you could implement.
But I don’t want Torollo to become a fake cloud provider.
The goal is much narrower:
Take an architecture concept and make enough of it real that the learner can be wrong about it.
If you say a database is isolated, there should be a way to test that claim.
If you introduce a cache, it should contain real state.
If a worker is supposed to process a queue, there should be an actual worker and queue involved.
If a failure is part of the exercise, something should really fail.
The local environment is there to make the consequences of the architecture visible, not to pretend that Docker on a laptop is equivalent to production infrastructure.
What Torollo includes right now
The current public version has three free roadmaps:
- a resilient three-tier application
- Redis cache-aside
- Redis queue workers
The roadmap format is JSON, so the exercises are not hardcoded into the application.
The core is MIT licensed.
It runs locally.
There is no account requirement.
You currently need:
- Node 18+
- Docker
Repository:
https://github.com/Derssa/Torollo
Three-tier roadmap:
https://torollo.app/r/devto
The question I’m trying to answer next
I think making the runtime real improves some parts of learning system design.
"PostgreSQL is private" stops being something written next to a box and becomes a statement that can actually be false.
But there is obviously a limit.
A local Docker environment does not reproduce IAM, managed services, cloud control planes, real latency, production observability, regional failures and all the other things that shape real systems.
So the question I’m trying to understand now is:
Where does making the runtime real improve the way you learn system design, and where does it start creating the wrong mental model?
If you work with backend systems, Docker, DevOps, SRE or infrastructure, that is the feedback I would be most interested in.

Top comments (2)
The useful part is making the checks fail loudly, especially around network boundaries. I’d add a test that drops the DB route or DNS briefly, then verify the app fails closed and recovers without manual cleanup.
Some comments may only be visible to logged-in visitors. Sign in to view all comments.