Library / SDK
libuv/libuv avatar
libuv/libuv

libuv: the C event loop behind Node.js, and how to build against it

Cross-platform asynchronous I/O

27,208 stars3,936 forksCMIT

At a glance

What is it?
libuv is a C library for asynchronous I/O that runs one event loop on epoll, kqueue, IOCP or event ports. It is MIT licensed, the last push to the v1.x branch was on 2026-09-16, and the documentation is thin on anything beyond the API reference.
Who is it for?
Adopt libuv when you are writing C or C++ and need one portable event loop across Linux, macOS and Windows instead of maintaining three backends yourself, and when you are willing to read the test suite as the real specification. Do not adopt it as a general task framework: the thread pool is sized for file system and DNS work, not for CPU-bound jobs, and it gives you no scheduler, no cancellation model and no memory management beyond what you write.
Can I use it commercially?
Yes. MIT is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
Is it still maintained?
Yes. The repository last received commits 1 day ago.
What is it written in?
Mainly C, according to GitHub's language statistics.

Answers come from the project's GitHub data, last synced on September 28, 2026, and from our analysis. They are not legal advice.

DEEP OPEN-SOURCE ANALYSIS

What libuv is for, and who ends up depending on it

The README describes libuv as a multi-platform support library with a focus on asynchronous I/O, and says it was primarily developed for use by Node.js. That origin explains the shape of the API. Node needed one way to wait on sockets, files, timers and child processes that behaved the same on Linux, macOS and Windows, where the underlying readiness mechanisms are entirely different. libuv is that layer. The README also lists Luvit, Julia and uvloop as users, so the audience is not only Node's runtime but any project that wants an event loop in C or C++ without writing per-platform code.

The feature list is worth reading as a statement of scope rather than a sales pitch. It covers an event loop backed by epoll, kqueue, IOCP and event ports; asynchronous TCP and UDP sockets; asynchronous DNS resolution; asynchronous file and file system operations; file system events; TTY handling through ANSI escape codes; IPC over Unix domain sockets or Windows named pipes; child processes; a thread pool; signal handling; a high resolution clock; and threading and synchronization primitives. That is a runtime substrate, not a framework. Nothing in that list decides how your program is structured.

If you are writing an application in a language with its own concurrency story, this library is probably not your direct dependency. It is the thing your runtime is built on. The people who should read the rest of this article are the ones writing C or C++ servers, embedding a scripting engine, or building a language runtime that needs portable I/O.

One loop, four backends, and a thread pool that is not a general worker pool

The architecture the README implies is a single event loop object that you create, register handles and requests with, and then run. Handles are the long-lived things: a TCP socket, a timer, a signal watcher, a process. Requests are one-shot operations. You register interest in readiness, call the run function, and the loop dispatches callbacks when the platform reports that something is ready.

The portability comes from the backend selection. On Linux the loop is backed by epoll, on the BSD family and macOS by kqueue, on Windows by IOCP, and on Solaris by event ports. Your code does not branch on that. This is the actual value proposition, and it is a large one: the alternative is maintaining four readiness implementations and getting the Windows one right, which is where most hand-rolled portable loops fail.

The thread pool is the part most people misunderstand. It exists because some operations have no readiness notification to wait on. File system calls are the clearest case: on most platforms there is no epoll or kqueue event that tells you a read from disk is ready, so the work has to be handed to a thread and the result posted back to the loop. DNS resolution has the same problem because the resolver APIs are blocking. The README lists the thread pool next to sockets and timers without saying what it is for, which is why it gets treated as a general-purpose worker pool. It is not. It is a fixed set of threads whose size you can configure, and if you queue long CPU-bound jobs onto it you will stall every file operation and DNS lookup in the process. There is no scheduler, no priority, and no preemption.

The README also notes that libuv follows semantic versioning from 1.0.0 and will keep a stable ABI across major releases. For a C library that is a meaningful commitment, and it is the reason bindings in other languages can track releases without constant breakage.

Building libuv and running a first loop

The README gives two build methods for Unix-like platforms including macOS: autotools, or CMake. Windows supports CMake only, and the README lists the prerequisites there as one of Visual C++ Build Tools, Visual Studio 2015 Update 3, or Visual Studio 2017, plus basic Unix tools for some tests, with Git for Windows suggested as a source of those tools.

The autotools path is the shortest on Linux and macOS. Run these from the repository root; the first script generates the configure script, and the last step installs the headers and library system-wide.

bash
sh autogen.sh
./configure
make
make check
make install

The CMake path is the one to prefer if you are consuming libuv from another CMake project, because it produces a target you can link against directly rather than a system install. The README's own invocation enables tests at configure time.

bash
cmake -B build -DBUILD_TESTING=ON
cmake --build build
(cd build && ctest -C Debug --output-on-failure)

If you would rather not build it, the README points at three package managers. Homebrew takes a HEAD install, vcpkg needs its own bootstrap step first, and Conan installs pre-built binaries or builds from source.

bash
brew install --HEAD libuv
conan install --requires="libuv/[*]" --build=missing

Once it is built, the honest starting point is not a tutorial. The README states that the tests and benchmarks serve as API specification and usage examples, which is a polite way of saying the prose documentation in docs/ is a reference, not a guide. The test list lives in test/test-list.h, and the test driver can run a single named test either forked into a child process or in the same process.

bash
build/uv_run_tests_a TEST_NAME

Some tests are timing sensitive, and the README documents an environment variable for relaxing timeouts on slow or overloaded machines. If a test fails on a busy CI runner before you have changed anything, this is the first thing to try.

bash
env UV_TEST_TIMEOUT_MULTIPLIER=2 build/uv_run_tests

Building the documentation itself uses Sphinx through a Makefile in the docs directory. The README lists HTML, live HTML, man pages and ePub targets, and notes that Windows users need make.bat instead of make.

Where libuv stops being the right tool

The thread pool is the sharpest limitation, and it is a design consequence rather than a bug. Because file system and DNS work is dispatched to a fixed pool, a program that blocks those threads with its own work will see unrelated I/O stall. The README does not frame the pool that way, but the feature list makes the dependency clear: asynchronous file operations and asynchronous DNS resolution sit in the same list as the pool itself. There is no backpressure mechanism described for the pool.

Cross-compilation is explicitly unsupported. The README includes a CMake invocation for cross-compiling to Windows with mingw and labels it unsupported but generally works. That is a fair description and also a warning: if your build pipeline depends on cross-compilation, you are outside what the project promises to keep working.

The documentation is a reference, not a curriculum. The README points at an external talk from 2012, a types-and-methods document, and a self-guided workshop, and then says those resources are not handled by libuv maintainers and might be out of date. For a library with this much surface area, that is a real cost. You will spend time in the test suite.

Finally, libuv is the wrong choice if what you actually want is a task framework. It gives you readiness notification and a place to put callbacks. It does not give you a scheduler, structured cancellation, or a memory model. If your problem is CPU-bound parallelism, the event loop is not the answer and the thread pool is not a substitute.

libuv against libevent and the platform APIs

The closest comparison people search for is libuv versus libevent, and the difference is in what each one is willing to own. libevent is an event notification library: you register file descriptors and timeouts, and it tells you when they are ready. libuv goes further and takes responsibility for the operations themselves. Asynchronous file system calls, DNS resolution, child processes and a thread pool to service the blocking parts are all inside libuv and are not part of libevent's scope.

That difference decides the choice. If you have an existing blocking API you want to wrap, or you want to keep control over how blocking work is dispatched, libevent leaves you the room. If you want one library that handles sockets, files, DNS, processes and signals with the same callback model across Windows and Unix, libuv is doing more of the work for you, and the thread pool is the price of that convenience.

The other alternative is the platform APIs directly: epoll on Linux, kqueue on macOS and the BSDs, IOCP on Windows. That gives you the most control and no dependency, and it costs you three or four implementations plus the Windows one, which is a different programming model from the Unix readiness APIs rather than a variation on them. For a project that only ever ships on Linux, going direct is defensible. For anything that ships on Windows too, libuv is usually the cheaper decision.

For Rust users, the uvloop and Julia entries in the README's user list are a reminder that libuv is normally consumed through a binding rather than called directly. The README does not document a Rust binding, so treat any specific crate as something to evaluate on its own.

Licence, maintenance and what an upgrade actually costs

libuv is MIT licensed. The README points at LICENSE and LICENSE-extra, and separately notes that the documentation is under CC BY 4.0 with its own LICENSE-docs file. Two licence files for the code is unusual enough to be worth reading before you vendor the source into a product, and the documentation licence is separate from the code licence, which matters if you intend to reproduce the docs. This is a description of what the repository contains, not legal advice.

Maintenance looks healthy on the evidence available. The repository is not archived, the last push to the v1.x branch was on 2026-09-16, and the most recent stable release is v1.52.1 from 2026-03-06, following v1.52.0 on 2026-02-11 and v1.51.0 on 2025-04-25. The README's versioning section states that libuv follows semantic versioning and keeps a stable ABI across major releases, so minor releases should not require recompiling dependent code.

Upgrade cost therefore sits mostly in the build, not the API. Release tarballs are signed, and the README documents verifying them with gpg, as well as verifying git tags. If you are pulling libuv into a regulated or audited build, that verification path exists and is documented, which is not true of every C library at this level of the stack.

The real cost is your own code's assumptions. Because the ABI is stable, a version bump will not break your build, which means a behavioural change in the event loop or thread pool can reach production without a compile error to warn you. Running the project's own test suite against the new version, with UV_TEST_TIMEOUT_MULTIPLIER set if your machine is slow, is the check the repository itself provides.

Editorial conclusion

Adopt libuv when you are writing C or C++ and need one portable event loop across Linux, macOS and Windows instead of maintaining three backends yourself, and when you are willing to read the test suite as the real specification. Do not adopt it as a general task framework: the thread pool is sized for file system and DNS work, not for CPU-bound jobs, and it gives you no scheduler, no cancellation model and no memory management beyond what you write. Before committing, verify two things against your own toolchain: that your build system resolves the CMake target names the way the repository's CMakeLists.txt defines them, and that the platform you ship on appears in SUPPORTED_PLATFORMS.md, because that file is the boundary the project itself draws.

Frequently asked questions

Is libuv written in C or C++?

It is a C library. The repository's primary language is C, and the README describes it as a multi-platform support library with a focus on asynchronous I/O.

How do you pronounce libuv?

The README does not give a pronunciation, and the repository does not document one.

Does Node.js use libuv?

Yes. The README states that libuv was primarily developed for use by Node.js, and lists Luvit, Julia and uvloop as other users.

What is libuv?

It is a multi-platform support library with a focus on asynchronous I/O, providing an event loop backed by epoll, kqueue, IOCP or event ports, along with sockets, DNS, file operations, processes and signals.

How do you install libuv?

You can build it from source with autotools using sh autogen.sh, ./configure, make and make install, or with CMake using cmake -B build. The README also documents Homebrew, vcpkg and Conan, and points at a downloads site for signed tarballs.

What is the libuv thread pool for?

It services the operations that have no readiness notification to wait on, which in the README's feature list means asynchronous file and file system operations and asynchronous DNS resolution. It is not described as a general-purpose worker pool for CPU-bound work.

Official sources

  1. libuv/libuv on GitHub
  2. License: MIT
  3. Project website
  4. README
  5. Releases
For maintainers

Add this badge to your README

If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.

Add this badge to your README

markdown
[![Hysen Labs](https://hysenlabs.com/badge/libuv-libuv.svg)](https://hysenlabs.com/projects/libuv-libuv)
Community notes

Community notes