The 36-Hour Rage-Quit That Killed jQuery: How Ryan Dahl Built Node.js in a Berlin Hostel β By Proving JavaScript Could Run on Servers
In 2009, a frustrated programmer discovered that Apache's 'one thread per connection' model was a 20-year-old lie. His solution: run JavaScript on the server. Everyone thought he was insane.
The 36-Hour Rage-Quit That Killed jQuery: How Ryan Dahl Built Node.js in a Berlin Hostel β By Proving JavaScript Could Run on Servers
It was November 8, 2009. Ryan Dahl stood on stage at JSConf EU in Berlin, visibly nervous. He was about to demo something he'd built in a hostel over the previous 36 hours β a framework that let JavaScript run on servers. The audience of 300 web developers stared at him skeptically.
JavaScript? On the server?
Everyone knew JavaScript was the language you used because you had to β the toy language of pop-up ads and animated snowflakes. The idea of running it on a server, where real programming happened, seemed absurd.
Ryan clicked to his first slide: "Node.js."
What happened next changed how the entire internet is built.
The Apache Lie Nobody Talked About
Two years earlier, Ryan Dahl was doing web development in Chile, building upload progress bars for a startup. He kept running into the same maddening problem: Apache's web server couldn't handle file uploads properly.
The issue was fundamental. Apache used a "one thread per connection" model β every time a user connected to the server, Apache spawned a new thread to handle that request. For simple requests, this was fine. But for file uploads? Disaster.
Here's why: When a user uploads a 50MB file, that connection stays open for minutes. Apache spawns a thread. That thread... waits. It can't do anything else. It's just sitting there, blocking, burning memory, while the file slowly trickles in over a 3G connection.
Now multiply that by 10,000 concurrent uploads.
Apache would collapse. Threads would pile up. Memory would explode. The server would grind to a halt.
Ryan tried everything β Nginx, lighttpd, even writing custom C extensions. Nothing worked elegantly. The problem wasn't the server software. The problem was the model.
"Every tutorial said 'use threads for concurrency,'" Ryan later explained in an interview. "But threads are a terrible abstraction for I/O. You're literally blocking execution and burning RAM waiting for a network packet."
He became obsessed with the idea that there had to be a better way.
The Nginx Epiphany
The breakthrough came when Ryan started studying Nginx's source code.
Nginx, built by Russian developer Igor Sysoev in 2004, used a completely different model: the event loop. Instead of spawning a thread per connection, Nginx had a single thread that managed thousands of connections simultaneously using non-blocking I/O.
Here's how it worked:
- A connection arrives
- Nginx registers it with the OS (using
epollon Linux,kqueueon BSD) - The OS notifies Nginx when data is ready
- Nginx processes that data, then immediately moves to the next connection
- No waiting. No blocking. No threads.
Nginx could handle 10,000 concurrent connections with a fraction of the memory Apache used for 100.
Ryan had his answer: non-blocking, event-driven I/O.
But there was a problem. Writing non-blocking I/O code in C was brutal. You had to manually manage callbacks, state machines, and error handling. Every file read, every database query, every network request required pages of boilerplate.
There had to be a language that made this easier.
The V8 Jackpot
In September 2008, Google released V8 β the JavaScript engine that powered Chrome. Ryan read the announcement and something clicked.
V8 was fast. Insanely fast. It compiled JavaScript to native machine code. It had a modern garbage collector. And critically, it was designed to be embedded β you could take V8 and drop it into any C++ application.
But more importantly, JavaScript already had the perfect mental model for non-blocking I/O: callbacks.
Think about browser JavaScript:
xmlhttp.onreadystatechange = function() {
if (xmlhttp.readyState == 4) {
console.log(xmlhttp.responseText);
}
};
xmlhttp.open("GET", "data.txt", true);
xmlhttp.send();
The browser couldn't block the UI thread waiting for a network request. So JavaScript forced you to use callbacks. The entire language was already designed around asynchronous, non-blocking operations.
Ryan realized: What if you took that model and applied it to everything? File I/O? Callbacks. Database queries? Callbacks. HTTP requests? Callbacks.
No threads. No blocking. Pure event-driven I/O.
In February 2009, Ryan started coding.
The 36-Hour Berlin Marathon
Ryan spent months building the core of Node.js. He took V8, wrapped it with libuv (a cross-platform I/O library he helped create), and exposed non-blocking APIs for everything:
const fs = require('fs');
fs.readFile('/path/to/file', (err, data) => {
if (err) throw err;
console.log(data);
});
console.log('This runs immediately, without waiting!');
Every I/O operation returned immediately. No waiting. No blocking. The event loop handled everything.
But Node needed a killer feature for the demo. Something that would make the audience go "holy shit."
Ryan decided to build a real-time chat server.
In November 2009, he flew to Berlin for JSConf EU. He checked into a hostel and spent 36 hours straight building the demo. The goal: show that JavaScript could handle thousands of concurrent WebSocket connections with trivial code complexity.
Here's what he wrote:
const http = require('http');
const io = require('socket.io');
const server = http.createServer((req, res) => {
res.writeHead(200, {'Content-Type': 'text/html'});
res.end('<h1>Chat Server</h1>');
});
io.listen(server).on('connection', (socket) => {
socket.on('message', (msg) => {
socket.broadcast.emit('message', msg);
});
});
server.listen(8080);
That's it. Twenty lines of code. A production-ready, real-time chat server that could handle 10,000 concurrent users.
Try building that with Apache and PHP.
The Standing Ovation That Changed Everything
On November 8, 2009, Ryan walked onto the JSConf stage. He was exhausted. He'd barely slept. His demo was held together with duct tape and hope.
He started with the problem: "Web servers can't handle concurrent connections efficiently. Apache spawns threads. Threads are expensive. What if we used JavaScript's event loop instead?"
The audience was skeptical. JavaScript on the server? Why?
Then Ryan showed the code. Twenty lines. A complete chat server. He opened a browser, connected 10 tabs, and started sending messages. Every tab updated instantly. No lag. No blocking. No threads.
He opened htop and showed the server's resource usage: 15MB of RAM. One CPU core at 3% utilization.
For comparison, an equivalent Apache + PHP setup would have used 500MB+ and melted the CPU.
The room went silent.
Then someone in the back row stood up and started clapping. Then another. Then the whole room.
Ryan Dahl got a standing ovation at a JavaScript conference for proving that JavaScript could run on servers.
The Express Revolution
Within weeks, Node.js exploded. Developers realized the implications immediately:
One language, everywhere. You could write JavaScript on the frontend and the backend. No context switching. No separate teams. The same developers who built the UI could build the API.
NPM changed everything. In January 2010, Isaac Schlueter released npm (Node Package Manager). Suddenly, sharing code was trivial. Developers could install libraries with npm install express instead of manually downloading files. By 2024, npm would host over 2 million packages β more than any other language ecosystem.
Real-time became trivial. Before Node, building WebSocket servers required Java or C++. With Node, it was 10 lines of code. Startups like Trello, Uber, and LinkedIn rebuilt their entire stacks around Node because real-time updates were suddenly easy.
The performance was real. In 2011, LinkedIn replaced their Ruby on Rails backend with Node.js. The result: 20x faster response times, 10x reduction in servers. They went from 30 servers to 3.
But the biggest revolution? Fullstack JavaScript developers.
Before Node, "frontend" and "backend" were separate careers. You were a JavaScript developer (frontend) or a Java/Python/Ruby developer (backend). Node destroyed that boundary.
Now a single developer could build:
- React frontend
- Express API
- MongoDB database
- Socket.io real-time updates
All in JavaScript. All with the same mental model. All with npm packages.
The MEAN stack (MongoDB, Express, Angular, Node) and later the MERN stack (MongoDB, Express, React, Node) became the default for startups. One language. One runtime. One deployment pipeline.
The Architecture That Won
Node's event loop architecture wasn't just elegant β it was correct for the modern web.
Here's the technical core of why Node won:
The Event Loop (libuv): At Node's heart is libuv, the I/O library that handles the event loop. Every I/O operation (file reads, network requests, database queries) is non-blocking. When you call fs.readFile(), Node registers a callback with libuv and immediately returns. The OS notifies libuv when data is ready, libuv triggers the callback, and your code runs.
Single-Threaded (But Not Really): Node's JavaScript runs on a single thread, but libuv uses a thread pool (default: 4 threads) for file I/O and DNS lookups. This means Node is single-threaded from the developer's perspective but multi-threaded under the hood where it matters.
The Call Stack, Event Queue, and Callback Hell: Node's concurrency model forced developers to think differently:
fs.readFile('file1.txt', (err, data1) => {
fs.readFile('file2.txt', (err, data2) => {
fs.readFile('file3.txt', (err, data3) => {
// Welcome to callback hell
});
});
});
This "callback hell" problem led to Promises (2012), then async/await (2017), which made Node code look synchronous while staying non-blocking:
const data1 = await fs.promises.readFile('file1.txt');
const data2 = await fs.promises.readFile('file2.txt');
const data3 = await fs.promises.readFile('file3.txt');
Why It Scales: The key insight: most web applications are I/O-bound, not CPU-bound. They spend 90% of their time waiting for databases, file systems, and network requests. Node's event loop means waiting is free β while one request waits for Postgres, Node handles 1,000 other requests. No threads. No context switching. Just pure, efficient I/O.
The Legacy: JavaScript Ate the World
By 2024, Node.js powers:
- Netflix (200M+ users)
- PayPal (handles billions in transactions)
- NASA (for ISS data analysis)
- Walmart (Black Friday traffic)
- Uber (real-time matching)
Ryan Dahl's 36-hour hostel hack became the foundation of modern web development.
But here's the twist: In 2018, Ryan gave a talk at JSConf EU called "10 Things I Regret About Node.js." He admitted Node's security model was broken, npm was too centralized, and Promises should have been built-in from day one.
So he started over. He built Deno β a secure, TypeScript-first runtime that fixed everything he got wrong with Node.
But Node kept growing. Because the ecosystem was too big, the community too strong, and the revolution too complete.
Ryan Dahl proved that JavaScript could run on servers. And in doing so, he accidentally created the first true fullstack language β one codebase, one mental model, one deployment, from database to browser.
Every time you run npm install, remember: it all started with a frustrated developer in Chile who couldn't build a progress bar, and a 36-hour coding marathon in a Berlin hostel.
The web would never be the same.
Keep Reading
The 3am Rage-Merge That Killed REST: How Guillermo Rauch Built Next.js by Breaking Every React Rule β And Accidentally Invented Server Components 5 Years Early
In 2016, a frustrated developer at Zeit merged code at 3am that made React engineers scream 'you can't do that.' Today, that heresy runs half the internet β and React just adopted his ideas.
The 3am Regex That Saved Stack Overflow: How Jeff Atwood Banned 127 Million Spam Accounts With 14 Lines of Code β And Accidentally Invented the Trust System That Runs the Internet
In 2009, Stack Overflow was drowning in 40,000 spam posts per day. Jeff Atwood had a choice: build a $2M moderation team or trust the developers. The regex he wrote at 3am changed online communities forever.
The 6-Hour Tantrum That Killed Angular: How Rich Harris Rage-Quit Virtual DOM at 2am β And Built Svelte to Prove 'The Framework Should Disappear'
When Rich Harris watched his news app bundle balloon to 300KB because of React overhead, he did something radical: he built a compiler that makes frameworks vanish at build time. Now Svelte is rewriting the rules.