How does libuv's thread pool work, and what uses it?

2 minadvancednodejslibuvthread-pooliouv-threadpool-size

Quick Answer

libuv keeps a small pool of background threads (default 4, set via UV_THREADPOOL_SIZE) for operations that have no async OS primitive — file system I/O, DNS lookups (getaddrinfo), and some crypto/zlib functions. Network I/O does NOT use the pool; it uses the OS's async facilities directly.

Detailed Answer

Answer: Not every asynchronous operation in Node is handled the same way. libuv splits work into two categories.

1. OS-level async (no threads needed): Network sockets use the operating system's event notification (epoll on Linux, kqueue on macOS, IOCP on Windows). These scale to many thousands of connections without extra threads.

2. The thread pool (for work with no async OS API): Some operations can only be done synchronously at the OS level, so libuv runs them on a pool of background threads and calls back when done:

  • File system operations (fs.readFile, etc.)
  • DNS resolution via dns.lookup / getaddrinfo
  • Some crypto functions (pbkdf2, randomBytes async forms)
  • zlib compression

Pool size:

  • Default is 4 threads. Configurable via the UV_THREADPOOL_SIZE environment variable (up to 1024), set before the process starts.
UV_THREADPOOL_SIZE=8 node app.js

Why it matters in interviews:

  • If you fire many concurrent fs/crypto/dns.lookup calls, they contend for those 4 threads — extra ones queue. This can cause surprising latency under load.
  • dns.lookup uses the pool, but dns.resolve (which queries DNS servers over the network) does not — a classic gotcha.
  • Raising UV_THREADPOOL_SIZE can help throughput for pool-bound workloads, but it doesn't help pure network I/O.