What is libuv and how does it enable async i/o in Node.js?
libuv: The Async I/O Engine of Node.js
libuv is a multi-platform C library that provides the event loop, thread pool, and async I/O capabilities that power Node.js. Without libuv, Node.js's non-blocking I/O model would not be possible.
Architecture Overview
ββββββββββββββββββββββββββββββββββββββββββββ
β Your Node.js Code β
ββββββββββββββββββββββββββββββββββββββββββββ€
β Node.js Core (JS APIs) β
β http, fs, crypto, net, dns, child... β
ββββββββββββββββββββββββββββββββββββββββββββ€
β Node.js Bindings (C++) β
ββββββββββββββββββββββββββββββββββββββββββββ€
β libuv (C library) β
β βββββββββββββββ ββββββββββββββββββββββ β
β β Event Loop β β Thread Pool β β
β β (I/O poll) β β (4 threads default)β β
β βββββββββββββββ ββββββββββββββββββββββ β
ββββββββββββββββββββββββββββββββββββββββββββ€
β Operating System β
β epoll (Linux) kqueue (macOS) IOCP β
ββββββββββββββββββββββββββββββββββββββββββββWhat libuv Provides
1. The Event Loop
The event loop polls for I/O events and dispatches callbacks when operations complete. It abstracts OS-specific async mechanisms:
- Linux:
epoll - macOS / BSD:
kqueue - Windows:
IOCP(I/O Completion Ports)
2. Thread Pool
Some operations can't be made async at the OS level (file system on some platforms, DNS lookups, crypto). libuv provides a thread pool (default: 4 threads) to handle these:
// These use the libuv thread pool:
fs.readFile() // file I/O
crypto.pbkdf2() // CPU-intensive crypto
dns.lookup() // DNS resolution
zlib compression // compression// Network I/O (TCP, UDP) does NOT use thread pool
// It uses OS async mechanisms directly (epoll/kqueue)
http.get() // no thread pool!
net.connect() // no thread pool!Thread Pool Size
# Default is 4 threads, can be increased:
UV_THREADPOOL_SIZE=16 node server.js// Or at runtime (must be set before any async work):
process.env.UV_THREADPOOL_SIZE = '16';Increase it when you have many concurrent:
- File system operations
crypto.pbkdf2()/crypto.scrypt()calls- DNS lookups
How an Async Operation Works
1. JS calls fs.readFile()
β
2. Node Binding passes request to libuv
β
3. libuv submits to thread pool (file) or OS (network)
β
4. Main thread continues (event loop runs)
β
5. Thread pool / OS completes the work
β
6. libuv queues the callback
β
7. Event loop picks up callback β executes in JSPractical Impact: Thread Pool Exhaustion
// If UV_THREADPOOL_SIZE=4 and you do:
for (let i = 0; i < 100; i++) {
crypto.pbkdf2(password, salt, 10000, 512, 'sha512', callback);
}
// Only 4 run in parallel! The rest queue up.
// Solution: increase UV_THREADPOOL_SIZElibuv Handles & Requests
libuv uses two abstractions:
- Handles β long-lived objects (TCP server, timers, file watchers)
- Requests β short-lived operations (write request, DNS lookup)
const server = net.createServer(); // Handle (long-lived)
server.listen(3000);
fs.readFile('./file', cb); // Request (one-time)Summary
libuv is the C engine behind Node.js. It implements:
- The event loop with OS-native async I/O polling
- A thread pool for blocking operations
- Handles and requests for managing async work
Understanding libuv helps you understand why Node.js is fast for I/O, why there's a thread pool limit, and how the event loop actually works.
Short Answer
Interview readyA concise answer to help you respond confidently on this topic during an interview.