Nephew is a developer. He writes code every day, ships features, fixes bugs — but somewhere along the way he never actually stopped to ask what his laptop is doing underneath his code. Tonight, uncle isn't in a hurry. He picks one building, one floor, one evening, and refuses to move to the next idea until this one actually makes sense.
The Building
👦 Nephew: Uncle, honestly, I feel a little embarrassed saying this — I write code every single day, I ship things to production, but I don't actually know what happens inside the machine when my code runs. Like, genuinely, at the ground level. I want to understand it properly. Not just definitions I can repeat in an interview — I want to actually feel it.
👨🦳 Uncle: That's not embarrassing at all, that's actually the most honest thing a developer can admit. Most people learn to drive a car without ever opening the hood. You've learned to write code without ever opening the laptop, so to speak. So tonight, let's open the hood properly. And I'm not going to rush. If something doesn't click, you stop me and ask, and we sit there until it clicks. Deal?
👦 Nephew: Deal.
👨🦳 Uncle: Good. So here's how I want you to picture it. Forget the laptop for a second. Imagine a big apartment building — say, 13 floors, dozens of flats. Real building, real people living their real lives inside it. Now, in any apartment building like this, what are the things everyone shares, that no single flat owns for themselves?
👦 Nephew: Hmm... water supply, I guess. Electricity. Maybe the lift.
👨🦳 Uncle: Exactly — water, electricity, the internet/cable connection coming in from a tower outside, the lift, maybe a shopping arcade or a small mall on the ground floor where different shops have rented space. Nobody owns the main water tank personally. Nobody owns the electrical transformer. There's a building management office — a facility manager — whose entire job is to take these shared, limited resources and distribute them fairly across every single flat, every single day, without anyone even noticing the effort involved.
👦 Nephew: Okay, I can picture that. So where's the connection to my laptop?
👨🦳 Uncle: Here's the thing — your laptop, right now, sitting in front of you, is exactly this. It has shared, limited resources. It has many "tenants" — your Chrome, your VS Code, your Node app, Spotify, whatever else is running. And it has one facility manager sitting quietly in the background making sure none of them collide, none of them starve, and none of them accidentally shut off water for the whole building. That facility manager has a name: the Operating System. On your machine, that's Ubuntu.
👦 Nephew: Wait — so Ubuntu is literally playing the role of that building manager?
👨🦳 Uncle: Precisely that role, doing precisely that job, every second your laptop is switched on. And tonight, we're going to walk through the building floor by floor, resource by resource, until you can see it as clearly as you'd see the actual water pipes and wiring in a real apartment block.
The Two Basic Resources — Before We Even Talk About the Manager
👨🦳 Uncle: Before we even bring the facility manager into the picture, let's understand the two most basic physical resources in this building. Because if you don't understand why these two exist and why they're different, nothing else after this will make real sense — you'll just be memorizing terms.
👦 Nephew: Okay, I'm listening.
👨🦳 Uncle: So — every apartment building needs two very different kinds of storage space, right? Think about it practically. There's the space where you keep things you use right now, today, within arm's reach — your kitchen counter, your desk. And there's the space where you store things long-term — a storeroom, a locker downstairs, maybe a warehouse if it's a bigger complex.
👦 Nephew: Sure, that makes sense in a normal house too.
👨🦳 Uncle: Right. Now here's the key difference between those two spaces — and I want you to really sit with this, because it explains almost everything else tonight. The kitchen counter is small, but it's instantly reachable. You don't walk anywhere, you just reach out and grab the thing. The storeroom is huge, you can keep years of stuff there, but every single time you need something from it, you have to physically get up, walk there, dig through boxes, and walk back. It's slower, by a lot.
👦 Nephew: Okay...
👨🦳 Uncle: Your laptop has the exact same two spaces, and they have names you already know but maybe never connected to this idea: RAM is the kitchen counter. SSD (your storage drive) is the storeroom.
SSD → the storeroom/warehouse. Huge. Permanent — things stay
there even if the power goes off. But slow to reach into.
RAM → the kitchen counter. Small. Temporary — the moment power
is cut, everything on it vanishes, wiped clean. But
blazing fast to reach into.
👦 Nephew: Wait, why does RAM lose everything when power goes off? That seems like a strange design. Why not just make one storage that's fast AND permanent?
👨🦳 Uncle: Ah — now you've asked exactly the right question, the one most people never even think to ask. Here's the honest, physical answer: RAM is fast because it's built out of tiny electrical circuits that hold a value only as long as electricity is actively flowing through them — like holding a ball up in the air with your hand. The moment you stop actively holding it up (cut the power), it falls — the value is gone. That's literally why it's so fast: there's no mechanical or chemical process involved, just electricity sitting in a circuit, ready instantly.
An SSD, on the other hand, stores information by physically trapping electrical charge inside tiny cells — a bit like locking a ball inside a small box. That box stays locked even without power, which is why it survives a shutdown. But locking and unlocking that box takes real physical time — more steps, more overhead — which is exactly why it's slower.
👦 Nephew: Oh — so it's genuinely a trade-off. Fast-but-forgetful, or slow-but-permanent. You can't easily get both.
👨🦳 Uncle: That's exactly it, and that trade-off is one of the oldest, most fundamental facts in all of computing. And because RAM is fast, it's also more expensive to manufacture per gigabyte — that's why your laptop probably has 8, 16, maybe 32 GB of RAM, but 512 GB or a full terabyte of SSD storage. You get a lot less of the fast, expensive counter space, and a lot more of the slow, cheap warehouse space. That balance is deliberate, in every computer ever built.
👦 Nephew: Okay, this is already making more sense than any explanation I've read before. So when I actually run my code — say, node index.js — what happens between these two?
👨🦳 Uncle: Good, hold that question — we're about to get there, but properly, step by step, not in a rush. First I want you to fully own this one idea: your code, right now, sitting saved in a file on your laptop, is sitting in the storeroom — the SSD. It's not doing anything. It's just sitting there, dormant, like a recipe written on paper inside a folder in the storeroom. For it to actually run, it has to be pulled out of the storeroom and placed on the kitchen counter — RAM — because only things on the counter can actually be worked with, actively, right now.
👦 Nephew: So "running" a program literally means: copy it from the SSD onto RAM?
👨🦳 Uncle: Essentially, yes — copy the necessary parts onto RAM, and then actively work with it there. And that copying, that whole decision of what goes onto the counter, how much space it gets, who's allowed to use the counter and for how long — none of that happens on its own. Someone has to manage it. And that's exactly where our facility manager, Ubuntu, walks back into the story.
Meet the Facility Manager
👨🦳 Uncle: So picture it again — the building. Ground floor has a small mall with shops. Floors 1 through 13 have flats, some occupied, some empty, tenants moving in and out all the time. Somewhere in the building, there's an office — small, unglamorous, but absolutely critical — where the facility manager sits, watching everything.
THE BUILDING (your laptop's hardware)
CPU workers, RAM counters, SSD storeroom,
shared electricity, water, network tower
│
┌─────────┴─────────┐
│ FACILITY MANAGER │ ← Ubuntu, the OS
│ (decides who gets │
│ what, and when) │
└─────────┬─────────┘
│
┌─────────────┬───────┴───────┬─────────────┐
│ │ │ │
Flat 3 Flat 7 Flat 11 Flat 13
(Chrome) (Node app) (Redis) (Spotify)
👦 Nephew: Okay, why does a building even need a facility manager, though? Why can't tenants just... sort it out themselves? Like, why can't my Node app just directly grab RAM whenever it wants?
👨🦳 Uncle: Because imagine what happens without one. Say tenant in Flat 3 decides they want the entire building's electricity for their personal use tonight, and just wires it directly to their flat, bypassing everyone else. What happens to the other twelve floors?
👦 Nephew: Blackout. Everyone else loses power.
👨🦳 Uncle: Exactly. Or imagine two tenants both try to plug into the exact same water pipe outlet at the exact same time, each assuming it's theirs alone. Chaos, leaks, conflicts. So the building enforces one absolute rule: no tenant touches shared infrastructure directly. Every single request — for water, electricity, network access, storeroom access — goes through the facility manager. Always. No exceptions, for anyone.
In computer terms, this has an actual name. The tenants — your applications — live in something called user space. They're not allowed to touch hardware directly. Only the facility manager — the OS — lives in kernel space, the one place with actual direct control over electricity, water, RAM, everything. Whenever your app needs anything from the hardware, it makes a polite, formal request called a system call — literally, knocking on the manager's office door and asking.
Your app: "Could I get a bit of RAM, please?" ──> Ubuntu (kernel space)
│
v
Ubuntu actually
talks to the RAM
hardware directly
│
v
Your app: "Thank you, here's my space" <───────────────┘
👦 Nephew: So even something as tiny as needing a bit of memory — my app can't just take it, it has to formally ask?
👨🦳 Uncle: Every single time, no matter how small the request. It feels invisible because it happens thousands of times per second and takes microseconds — but structurally, that knock on the door happens, every time. Hold onto this rule tightly, because literally everything else tonight is built on top of it.
A New Tenant Moves In
👨🦳 Uncle: Alright. Let's finally bring in your actual example. You open your terminal, you type:
npm start
You hit Enter. Before anything else happens — just pause there for a second. What do you think is the very first thing that needs to happen, based on everything we've just said?
👦 Nephew: I guess... my code needs to move from the storeroom, the SSD, onto the kitchen counter, the RAM?
👨🦳 Uncle: Very good — you're already thinking in the right direction. But even before that copying happens, there's a step before the step. Ubuntu first has to decide: "Is this a completely new tenant moving into the building, or an existing one?" In this case, it's new. So Ubuntu creates what's called a process — think of it as officially registering a brand-new tenant into a brand-new, empty flat. This tenant gets a unique flat number (in computing, it's called a PID — Process ID), and a private slice of RAM that belongs only to them — nobody else in the building, no other tenant, can see inside it or touch it.
$ npm start
│
v
Ubuntu: "New tenant moving in. Let's set them up properly."
│
├── Assign a unique PID (like flat number 4521)
├── Go to the storeroom (SSD), pull the Node code off the shelf
├── Copy it onto a fresh, private RAM counter
└── Officially mark this tenant as "now living here, running"
👦 Nephew: Why does it need to be private, though? Why can't two tenants just share the same RAM space if they trust each other?
👨🦳 Uncle: Excellent question — and here's the real-world reasoning. Imagine two tenants shared one open flat with no walls between them. If one tenant's kid accidentally breaks something, it affects the other tenant too, even though they had nothing to do with it. Now translate that to software: if one process could freely read or write into another process's memory, one buggy or crashed program could corrupt or crash a completely unrelated one. Isolation isn't bureaucracy for its own sake — it's what stops one tenant's mess from becoming everybody's problem.
# You can literally see the current tenant list yourself
$ ps aux
USER PID %CPU %MEM COMMAND
you 4521 2.1 1.5 node index.js
you 4522 0.0 1.4 node index.js # a completely separate tenant
👦 Nephew: Oh interesting — so if I ran npm start in two different terminal tabs, I'd actually get two totally separate flats, even though it's literally the same code?
👨🦳 Uncle: Exactly two separate PIDs, two separate private RAM slices, two independent tenants — even though, ironically, they're running identical code. If one crashes, the other doesn't even notice.
Family Members Sharing One Flat — What's a Thread, Really?
👦 Nephew: Okay wait, before we go further — I keep hearing "threads" thrown around alongside "processes," and honestly I've always just nodded along without really knowing the difference.
👨🦳 Uncle: Fair, let's fix that properly. If a process is one flat, a thread is like a family member living inside that flat. They share the same furniture — the same RAM — but each one can be doing something different, at the same moment, moving around the flat independently.
Process (one flat, one private RAM space)
│
├── Thread 1 — one family member, cooking
├── Thread 2 — another family member, cleaning
└── Thread 3 — a third, watching TV
All three share the SAME furniture (RAM) — but each is doing
a different task, at the same time, without waiting on the others
👦 Nephew: So a program that's "single-threaded" is like a flat with only one person living there, doing one thing at a time?
👨🦳 Uncle: Exactly that picture. One person, one task, in sequence — cook, then clean, then watch TV, never simultaneously. A "multi-threaded" program is a flat with several capable family members, each independently handling something, genuinely at the same time.
Why "Node Is Single-Threaded" Confuses Everyone (Including You, Clearly)
👦 Nephew: This is the one that's genuinely bugged me for years. Everyone repeats "Node.js is single-threaded" like scripture, but I've personally seen Node handle thousands of concurrent requests without falling over. If there's really just one person in that flat, how do they handle a thousand things at once without everything grinding to a halt?
👨🦳 Uncle: Because that one person has learned a very specific trick — they never, ever stand around waiting idle for something slow to finish. Let me walk you through it with a very human example first, then we'll bring it back to code.
Imagine that one family member in the flat is cooking. They put rice on to boil — that takes fifteen minutes. Does a sensible cook just stand there, motionless, staring at the pot for fifteen minutes, doing nothing else?
👦 Nephew: No, obviously not. They'd go chop vegetables, or start something else, and come back to the rice when it's ready.
👨🦳 Uncle: That's precisely what Node.js does, and it's the entire secret behind its concurrency. Your actual JavaScript code genuinely does run on just one thread — one "cook" — handling instructions one at a time, never two lines simultaneously. That much of the "single-threaded" claim is completely, literally true.
Your JavaScript code:
ONE main thread reads and runs your instructions, one line
at a time, in order — never two lines truly at once
But whenever that main thread hits something slow — reading a file from the storeroom, waiting for a database reply, waiting on a network request — it doesn't freeze and wait. It hands that slow task off to a quiet background crew, and immediately moves on to the next task in line, exactly like the cook walking away from the boiling rice to chop vegetables.
Your code: "Please go read this file for me" ──────> handed off to
the background
crew (a thread
pool, managed
by a library
called libuv)
Meanwhile, the main thread IMMEDIATELY moves to the next
task in line — doesn't sit idle waiting
Later: "Sir, the file is ready!" ─────> main thread picks up the
result and continues right
from where that task left off
👦 Nephew: So there genuinely ARE multiple threads at work — just not for my actual business logic, only for the "go wait on something slow" parts?
👨🦳 Uncle: Exactly the nuance almost everyone misses. This whole arrangement — one main thread for your code, a background crew quietly handling the waiting — is called Node's event loop, and the library doing that background coordination is libuv. When people say "Node is single-threaded," they specifically mean your JavaScript logic. The full Node runtime, underneath, is quietly multi-threaded the entire time.
Back to RAM — How Much Does a New Tenant Actually Get?
👦 Nephew: Okay, coming back to something you said earlier — when my process starts, does it just grab as much of the kitchen counter, the RAM, as it feels like?
👨🦳 Uncle: No, and this is worth understanding slowly, because it explains a lot of memory-related bugs you've probably hit without realizing why. When Ubuntu creates your new tenant, it doesn't hand over a huge chunk of counter space upfront "just in case." That would be wasteful — imagine giving every new tenant in the building a massive kitchen the size of a restaurant, most of which sits unused. Instead, Ubuntu gives a small, modest initial slice — just enough to get the program started.
$ npm start
│
v
Ubuntu gives a SMALL initial slice of RAM — enough to load
and start the program, not the full amount it might eventually need
│
v
As your program runs and genuinely needs MORE (say, you load a
huge array, or open hundreds of connections), it goes back and
asks Ubuntu again: "I need more counter space" — and Ubuntu
extends it, usually in sensible, sizeable chunks
👦 Nephew: So RAM usage growing over time while my app runs — that's literally just this process happening again and again?
👨🦳 Uncle: Exactly that, repeated continuously. And within that slice of counter space, there are actually two very different areas worth knowing by name, because they behave completely differently.
THE STACK — a small, extremely fast, strictly ordered area.
Used for short-lived things: function calls, local variables.
Picture a stack of plates — last one placed is the first one
removed. The moment a function finishes, its plate is
automatically, instantly cleared away.
THE HEAP — a larger, more flexible area.
Used for things that need to stick around longer, or whose
size isn't known upfront — objects, arrays, anything you
create with 'new' or as a literal. More flexible, but nobody
automatically clears it away the instant you're "done" —
which is exactly where garbage collection comes in.
👦 Nephew: Garbage collection — that's the automatic memory cleanup thing, right? I've always used it without really understanding what it's doing.
👨🦳 Uncle: Think of it like this — periodically, Ubuntu (through Node's runtime, in this case) walks through the heap area of the kitchen counter and asks, item by item: "Is anyone in this flat still actually using this piece of furniture?" If genuinely nothing in your running program still references it, it gets quietly cleared away, freeing up counter space for new things. It happens automatically, in the background, without you lifting a finger — one of the real conveniences of a managed runtime like Node's, compared to older languages where the tenant has to manually throw out their own garbage.
$ node --max-old-space-size=4096 index.js
👦 Nephew: What's actually happening when I set that flag?
👨🦳 Uncle: You, the tenant, are walking up to Ubuntu ahead of time and saying: "please let my heap grow up to 4GB if I genuinely need it." A deliberate ceiling you're choosing on purpose — not something to set carelessly, because a flat allowed to expand its counter space endlessly starts eating into space every other tenant in the building needs too.
The Shared Elevator — How the CPU Actually Gets Divided
👦 Nephew: Okay, new question. If I've got Chrome, my Node app, Spotify, and who knows what else running at the same time, but my laptop only has a handful of CPU cores — how is everyone actually doing work at once?
👨🦳 Uncle: Here's the slightly surprising truth: they're often not actually doing work at the exact same physical instant, if you've got more tenants than cores. Think of each CPU core as the one shared lift in this 13-floor building. Multiple tenants want to use it right now, but physically, only one person can actually be riding inside it at any given moment.
Ubuntu's solution — and this is genuinely clever — is to give each tenant a very short, fair ride in the lift, then immediately swap to the next tenant waiting. It does this so fast, so many thousands of times per second, that from each tenant's own perspective, it feels like they have the lift constantly, personally, available to them.
One real CPU core, over a tiny slice of time:
|--Tenant A--|--Tenant B--|--Tenant C--|--Tenant A--|--Tenant B--|...
(a few (a few (a few (back to ...
milliseconds) milliseconds) milliseconds) A already)
Switches happen thousands of times per second — so fast that
everyone FEELS like they're running continuously and simultaneously,
even though, strictly, only one tenant is truly active on that
core at any single instant.
👦 Nephew: And who decides the order, and how long each tenant's turn lasts?
👨🦳 Uncle: That's Ubuntu's scheduler — genuinely, it's doing the exact same job a load balancer does across a whole fleet of servers, deciding fairly who gets a limited shared resource, right now, and for how long. Except here it's happening inside one machine, not across a network of them. Same underlying problem, wildly different scale.
The Network Tower — How Requests Actually Reach Your App
👦 Nephew: Okay, this next part I actually deal with all the time as a backend guy, but I've never really thought about it from this angle. Where does the network — the internet connection — fit into this whole building picture?
👨🦳 Uncle: Think of the network card in your laptop as the building's one connection point to an external cable or tower — the single physical wire or antenna where data from the outside world actually enters and leaves the building. But here's the important nuance: that connection point itself has no idea which tenant an incoming delivery is actually meant for. That decision is Ubuntu's job, and it's made based on the delivery's label — specifically, the port number.
Data arrives at the network tower / loading dock (network card)
│
v
Ubuntu checks the label on the delivery: "This is addressed
to port 3000"
│
v
Ubuntu checks its own internal notebook: "Who's currently
registered as listening on port 3000?" → "Ah — tenant PID
4521, the Node app in Flat 7"
│
v
Ubuntu personally delivers the data straight to that tenant's flat
👦 Nephew: So when I write app.listen(3000) in my Node code, what's actually happening behind that one line?
👨🦳 Uncle: Your process is walking up to Ubuntu's office and formally saying: "I would like to claim door number 3000 in this building — please send anything addressed there directly to me, and to no one else." Ubuntu writes this down in its own internal record. From that exact moment forward, anything arriving labeled "port 3000" goes specifically, only, to your process.
When Two Tenants Fight Over the Same Door
👦 Nephew: Oh, this actually happened to me last week! I ran npm start, forgot the first one was still running in another tab, ran it again — and got this angry red error about the address already being in use. What was actually happening there?
👨🦳 Uncle: Beautiful, real example — let's slow right down and walk through it properly, because this is one of those errors developers see constantly but rarely actually understand. A door — a port — in this building can only ever be claimed by one tenant at a time. Ubuntu enforces this as an absolute rule, specifically so there's never any confusion about which flat a delivery is meant for.
Tenant A (your first npm start): "I'd like to claim port 3000"
│
v
Ubuntu: "Granted. Port 3000 now belongs to you, tenant A."
Tenant B (your second npm start, same port): "I'd also like port 3000"
│
v
Ubuntu: "Sorry — port 3000 is ALREADY claimed by tenant A. Denied."
│
v
Tenant B's process crashes immediately, with:
Error: listen EADDRINUSE: address already in use :::3000
👦 Nephew: So EADDRINUSE is basically Ubuntu saying, word for word, "this address is already being used by someone else, I can't give it to you too"?
👨🦳 Uncle: Exactly what it says, read completely plainly — there's no hidden mystery in that error message once you see the building picture. And there are exactly two honest ways out of it: free up the door — stop the first tenant so the door becomes available again — or ask for a different door entirely, running the second instance on, say, port 3001 instead.
# Find out exactly WHO currently holds port 3000
$ lsof -i :3000
COMMAND PID USER ...
node 4521 you ...
# Free up the door by asking that tenant to leave
$ kill 4521
# Port 3000 is available again
👦 Nephew: And once I kill that process, does Ubuntu actually go clean everything up properly — the RAM, the claimed port, everything that tenant was using?
👨🦳 Uncle: Yes, completely — the very moment a tenant moves out, whether they left politely or were forced out, Ubuntu reclaims everything that had been allocated to them: the RAM counter space is freed up for the next tenant, the claimed port becomes available again, any open files get closed. Nothing stays permanently stuck in limbo just because one tenant left in a hurry — thorough cleanup after every departure is a core, non-negotiable part of the facility manager's job.
Living Together Without Stepping on Each Other's Toes
👦 Nephew: So if I've got my Node app, Redis, and Postgres all running on the same laptop at once — how do they genuinely coexist without interfering with each other?
👨🦳 Uncle: Same building, same rules we've walked through all evening. Three separate tenants, each with their own private, isolated slice of RAM, each potentially claiming their own separate door — Node might take port 3000, Redis defaults to 6379, Postgres to 5432 — and each getting fair, rapidly rotating turns on that one shared lift, courtesy of the scheduler we just discussed.
Tenant: Node app → RAM: 150MB Door (port): 3000
Tenant: Redis → RAM: 80MB Door (port): 6379
Tenant: Postgres → RAM: 200MB Door (port): 5432
None of them can see into each other's flat.
None of them can claim a door someone else already holds.
All of them get fair, fast-rotating turns on the one shared lift.
👦 Nephew: And if one of them starts misbehaving — say a memory leak quietly builds up in my Node app over hours — does Ubuntu notice on its own, or does it just let that one tenant slowly take over the whole building?
👨🦳 Uncle: Ubuntu absolutely keeps watch, constantly — that's a core, ongoing part of the job, not something optional it only does occasionally. You can actually watch this monitoring happen live, right now, on your own machine:
$ top
# or, a friendlier version:
$ htop
PID USER %CPU %MEM COMMAND
4521 you 45.2 12.4 node index.js <- using a LOT of CPU right now
4390 you 0.5 3.1 redis-server
4200 you 1.2 8.7 postgres
Ubuntu is measuring, continuously, exactly how much CPU time and RAM every single tenant in the building is actually consuming, second by second — top and htop are simply showing you the same numbers Ubuntu is already tracking internally, all the time. Now — if a tenant genuinely starts running away with RAM (a leak) or CPU (an infinite loop somewhere), Ubuntu doesn't usually step in and forcibly evict them on its own. What it does do is give you, the building's actual owner, complete visibility into exactly who's misbehaving, so you can notice it and personally intervene — the way top just clearly showed you tenant 4521 quietly hogging resources.
Your Direct Phone Line to the Manager
👦 Nephew: One more thing that's always felt slightly mysterious — the terminal itself. When I type commands there, what actually is that black window, underneath?
👨🦳 Uncle: Think of the terminal, and the shell running inside it — on Ubuntu, that's usually bash — as your own direct, private phone line straight to the facility manager's office. Instead of walking around clicking on icons and buttons, you're speaking directly, in a precise language Ubuntu understands immediately, with absolutely no translation layer sitting in between you and the manager.
You type: npm start
│
v
The SHELL (your private phone line to the manager) reads that
line, works out exactly what program you mean, and formally asks
Ubuntu: "Please create a new process running this program"
│
v
Ubuntu does exactly what we walked through earlier — creates
the tenant, allocates RAM, gets everything running
👦 Nephew: Wait — so the shell itself is also just a tenant, a process, like everything else we've talked about tonight?
👨🦳 Uncle: Exactly — the shell is an entirely ordinary tenant itself, with its own PID, its own small private RAM slice, exactly like any other process in the building. Its one specific job is interpreting whatever you type and formally asking the facility manager to carry it out on your behalf. When you typed npm start earlier tonight, the shell was the one that actually walked up and made that request to Ubuntu, the instant you hit Enter.
The Shopping Mall Downstairs — What Docker Is Actually Doing
👦 Nephew: Okay, save the best for last — Docker. I've used it for years, I know the commands, but I've genuinely never had someone explain to me what it's actually doing underneath, in a way that made real sense. And how is it actually different from a full Virtual Machine? I've always just kind of nodded at that question in interviews.
👨🦳 Uncle: Let's finally settle this properly, because the difference is real and specific — not marketing language, and definitely not something to just nod along to anymore. Remember that small shopping arcade on the ground floor of our building? Imagine you want to open a little food stall there. You don't build a whole new building for your stall — the electricity, water, structural walls, everything already exists, provided by the main building. You simply rent a small, self-contained unit: your own counter, your own equipment, your own inventory, walled off from the stall right next to you — but you're still fundamentally plugged into, and dependent on, the same building's existing infrastructure underneath you.
That's exactly what a Docker container is. It shares the same underlying building — the host machine's real Ubuntu kernel, the one actual facility manager — but gets its own isolated, temporarily-rented little space: its own files, its own view of running processes, its own view of the network, walled off from other stalls, without anyone needing to construct an entirely separate building underneath it.
DOCKER (temporary rented stalls inside the ONE existing mall):
THE ACTUAL BUILDING (Host Ubuntu Kernel — the one real manager)
│
┌─────────────────────┼─────────────────────┐
│ │ │
Stall 1 Stall 2 Stall 3
(Container A, (Container B, (Container C,
rented, isolated) rented, isolated) rented, isolated)
ALL sharing the SAME real facility manager (host kernel) underneath
👦 Nephew: And a Virtual Machine — how's that genuinely different, then?
👨🦳 Uncle: A Virtual Machine is a far more drastic move. Instead of renting a small stall in an existing mall, imagine constructing an entirely separate building — with its own full electrical system, its own plumbing, its own everything, including its own complete, independent facility manager — even though it happens to be sitting on the exact same plot of physical land as the original building.
VIRTUAL MACHINE (a whole separate building, same land):
THE PHYSICAL LAND (actual hardware)
│
┌────────┴────────┐
│ HYPERVISOR │ ← divides the land itself
│ (landlord over │ between buildings
│ the buildings) │
└────────┬────────┘
┌────────────────┼────────────────┐
│ │
BUILDING 1 (Host Ubuntu) BUILDING 2 (Guest OS)
its own full facility manager its OWN full, separate
running as usual facility manager — a
complete second OS
👦 Nephew: So a VM has to actually construct and boot up an entire second building, with its own second manager, while Docker just rents space inside the manager that's already up and running?
👨🦳 Uncle: That's exactly the core difference, and it explains everything else you've probably already noticed in practice. A VM is genuinely heavier and slower to start — you're building a whole new building with its own manager before anything inside it can even begin. A Docker container is dramatically lighter and faster to start — you're just renting an already-sectioned-off space inside a building whose manager is already wide awake and running, so there's no separate startup needed at all.
But Does Docker Still Use My Real RAM and CPU?
👦 Nephew: Okay but here's something that's genuinely bothered me — if a container doesn't have its own separate manager, its own separate OS, and it's running on my real laptop's real hardware... is it still using my real RAM, my real CPU, through my real Ubuntu?
👨🦳 Uncle: Entirely, completely yes — and this is exactly the part people quietly misunderstand, thinking Docker is somehow its own totally self-contained little world. It isn't. Every container, underneath all its clever-looking isolation, is still, as far as your real facility manager — your actual host Ubuntu — is concerned, just an ordinary tenant, an ordinary process (or a small handful of them). It gets RAM allocated from your real kitchen counter, gets CPU turns on your real shared lift, and can claim a real door on your real network tower. Docker simply adds an extra layer of labeling and boundaries on top — using specific Ubuntu kernel features called namespaces and cgroups — that make each stall believe it has the entire mall to itself, while the real facility manager quietly, continuously enforces the actual sharing and limits underneath, invisibly.
# You can literally see a running container as an ordinary process,
# right alongside everything else, from the HOST's own perspective:
$ ps aux | grep docker
USER PID %CPU %MEM COMMAND
you 5102 3.2 4.1 (a process belonging to your running container)
# You can even explicitly cap how much of your REAL RAM and CPU a
# container is allowed to use — exactly like giving a mall stall a
# strict electricity and floor-space quota it can never exceed:
$ docker run --memory="512m" --cpus="1.0" my-node-app
👦 Nephew: So the entire point of Docker was never really "a separate computer living inside my computer" — it's actually "a neatly packaged, portable little set of instructions and dependencies, temporarily renting space as an ordinary, lightweight tenant of the one real Ubuntu underneath"?
👨🦳 Uncle: That's precisely the right way to hold this in your head, going forward. Docker was genuinely never really about achieving deeper isolation than a Virtual Machine gives you — its real, honest intention has always been portability and consistency. Packaging up an application together with its exact dependencies, exact versions, exact configuration, so it runs identically wherever the same underlying kernel and Docker engine exist. A stall you can pack up, carry, and reopen — identically — in any mall that happens to have the same basic infrastructure, without ever having to rebuild it from scratch each time you move.
Walking the Whole Story, One Last Time
👨🦳 Uncle: Alright, we've covered a lot of ground tonight, so let's walk the entire journey once more, start to finish, tying every single piece together.
$ npm start
│
v
The SHELL (your private phone line to Ubuntu) reads your command,
formally asks Ubuntu to act on it
│
v
Ubuntu goes to the SSD storeroom, pulls your code off the shelf,
creates a NEW PROCESS — a new tenant, unique PID, fresh flat
│
v
Ubuntu allocates an initial slice of RAM — the fast kitchen counter
— stack for quick function calls, heap for everything longer-lived,
growing further as your app genuinely asks for more
│
v
Node's runtime starts up — ONE main thread runs your actual
JavaScript, while a background thread pool (libuv) stands ready to
quietly handle slow file/network waiting on your behalf
│
v
Your code runs: app.listen(3000)
│
v
The process asks Ubuntu to CLAIM port 3000. If another tenant
already holds it — EADDRINUSE, request firmly denied. If it's
free — granted, and Ubuntu now knows exactly which flat to
route port-3000 traffic to
│
v
From this point on, Ubuntu's SCHEDULER gives this tenant fair,
rapid turns on the shared CPU lift, alongside every other running
tenant — Chrome, Redis, Postgres, anything else currently active
│
v
A request eventually arrives at the network tower. Ubuntu checks
the label — port 3000 — and delivers it straight to your process's flat
│
v
Your app processes the request, using its allocated RAM and its
share of CPU turns, and sends the reply back out the same way it
arrived — through the network tower
│
v
Meanwhile, Redis and Postgres, if they're running, are quietly
doing their own independent work in their own separate flats,
entirely unaware of your Node app's activity, all managed
fairly, all the time, by the same one facility manager: Ubuntu
👦 Nephew: Honestly, uncle, I feel like I actually understand my own laptop now, for the first time, not just recite the words. One command — npm start — quietly triggers a new tenant moving in, RAM being handed out on request, threads and a background crew cooperating, a door being formally claimed, and continuous, fair CPU scheduling — all orchestrated by one facility manager, the entire time my app just sits there running.
👨🦳 Uncle: Every single time, for every single program you've ever run, on every machine you've ever touched. And a server in a data centre somewhere, running your production code right now — it's doing this exact same story, just usually with a lot more RAM, a lot more CPU cores, and a lot more tenants moving in and out at once. The building gets bigger, but the facility manager's job never fundamentally changes. It feels invisible and effortless purely because Ubuntu is extraordinarily good at its job — quietly juggling dozens of tenants, RAM requests, and CPU turns, every fraction of a second, without you ever having to think about any of it directly, unless something goes wrong, and now — it won't feel like magic anymore when it does.
Next time: what happens when one building genuinely isn't enough anymore — how multiple machines coordinate together as one cluster, and how Kubernetes plays facility manager over an entire estate of buildings, not just one.
Top comments (0)