Node.js Compatibility

Bun's compatibility status with Node.js APIs, modules, and globals

Every day, Bun gets closer to 100% Node.js API compatibility. Popular frameworks like Next.js, Express, and millions of npm packages intended for Node.js work with Bun. To ensure compatibility, we run thousands of tests from Node.js' test suite before every release of Bun.

If a package works in Node.js but doesn't work in Bun, we consider it a bug in Bun. Open an issue and we'll fix it.

We update this page regularly. It reflects the latest version of Bun's compatibility with Node.js v26.

Built-in Node.js modules#

node:assert#

🟒 Fully implemented. Legacy-mode deepEqual uses Bun.deepEquals semantics rather than Node's loose == comparison, and function-valued or printf-style message arguments are not formatted.

node:buffer#

🟒 Fully implemented. A single Buffer is capped at 4 GiB (buffer.constants.MAX_LENGTH is 2**32).

node:console#

🟒 Fully implemented. Bun writes console output directly to the stdout/stderr file descriptors and formats it with its own inspector. As a result, replacing process.stdout.write does not capture the output, and object layout differs from util.inspect. console.trace() writes to stdout and console.time*() to stderr.

node:dgram#

🟒 Fully implemented. 99% of Node.js's test suite passes. addMembership() does not implicitly bind an unbound socket; call bind() first.

node:diagnostics_channel#

🟑 channel(), subscribe(), tracingChannel() and the http client, http2 and dgram built-in channels are implemented. Missing boundedChannel() and the http.server.*, net, module, console, child_process and worker_threads built-in channels. Subscribers do not keep a Channel alive, so hold a reference to it.

node:dns#

🟒 Fully implemented. Missing resolveTlsa. Bun ignores the Resolver maxTimeout option.

node:events#

🟒 Fully implemented. 95% of Node.js's test suite passes. EventEmitterAsyncResource uses AsyncResource underneath, so its asyncId is always 0.

node:fs#

🟒 Fully implemented. 98% of Node.js's test suite passes. Stats objects lack the Temporal.Instant getters (atimeInstant and friends).

node:http#

🟒 Fully implemented. http.Server does not extend net.Server. Bun ignores listen(handle) and the fd, ipv6Only and signal options of listen(). keepAlive/keepAliveInitialDelay on the server are no-ops.

node:https#

🟑 request, get, Agent and globalAgent are implemented, including connection pooling. Client sockets are tls.TLSSockets. https.Server is http.Server with TLS options rather than a tls.Server. Its request sockets (req.socket) are not tls.TLSSockets: encrypted, authorized and servername work, but getPeerCertificate() and getCipher() are missing. setSecureContext(), addContext(), SNICallback and handshakeTimeout are not supported.

node:os#

🟒 Fully implemented. userInfo() reads username, shell and homedir from the environment (USER, SHELL, HOME) rather than the passwd database. machine() returns "arm64" instead of "aarch64" on Linux arm64.

node:path#

🟒 Fully implemented. matchesGlob() uses Bun.Glob semantics rather than minimatch (* matches dotfiles, no extglobs). path.win32 differs from Node in a few edge cases involving device paths and reserved names.

node:punycode#

🟒 Fully implemented. 100% of Node.js's test suite passes. Deprecated by Node.js.

node:querystring#

🟒 Fully implemented. 100% of Node.js's test suite passes.

node:readline#

🟒 Fully implemented.

node:stream#

🟒 Fully implemented. isReadable, isWritable, isErrored and Readable.isDisturbed only understand Node.js streams, not web streams.

node:string_decoder#

🟒 Fully implemented. 100% of Node.js's test suite passes. end() does not accept a string argument.

node:timers#

🟒 Fully implemented. The exports are the same functions as the globals. node:timers/promises (including scheduler.wait() and scheduler.yield()) is also implemented.

node:tty#

🟒 Fully implemented. ReadStream and WriteStream extend the fs streams rather than net.Socket, and constructing them on a non-TTY fd returns a stream with isTTY set to false instead of throwing.

node:url#

🟒 Fully implemented.

node:zlib#

🟒 Fully implemented. 98% of Node.js's test suite passes.

node:async_hooks#

🟑 AsyncLocalStorage and AsyncResource are implemented. createHook, executionAsyncId, triggerAsyncId and executionAsyncResource are stubs: Bun does not invoke hooks, apart from init for process.nextTick, and async ids are always 0. Node.js strongly discourages these APIs in favor of AsyncLocalStorage. Bun does not propagate AsyncLocalStorage context into MessagePort, BroadcastChannel or Worker events.

node:child_process#

🟑 IPC can send net.Socket, net.Server and dgram.Socket handles (including to and from Node.js processes), but not http server sockets. serialization: "advanced" only works between Bun processes, so use JSON serialization for Node.js ↔ Bun IPC. Missing subprocess.channel.ref()/unref(). You cannot pass a child's stdout/stderr as another child's stdio, and spawnSync does not return extra stdio pipes in output.

node:cluster#

🟑 net and dgram servers in workers are shared through the primary as in Node.js (SCHED_RR and SCHED_NONE), and handles can be passed with worker.send(). node:http/node:https servers in workers each bind their own socket instead, so load-balancing HTTP requests across processes is only supported on Linux (through SO_REUSEPORT). Otherwise, implemented but not battle-tested.

node:crypto#

🟑 Missing encapsulate/decapsulate (you can use ML-KEM keys through crypto.subtle). argon2() and argon2Sync() are implemented. Custom engines (setEngine()) throw, setFips() is a no-op and secureHeapUsed() returns undefined. Bun's crypto is backed by BoringSSL, which lacks the ed448, x448, rsa-pss, dsa, dh and ml-kem-512 key types, EC curves other than P-224/256/384/521 (no secp256k1), and the CCM, OCB, XTS and chacha20-poly1305 ciphers. The DiffieHellman class (createDiffieHellman(), getDiffieHellman()) works.

node:domain#

🟑 Missing Domain members. A domain only catches errors thrown synchronously inside run()/bind() or emitted by emitters passed to add(). Bun does not route errors from timers, process.nextTick, promises and other async callbacks to the domain.

node:http2#

🟒 Client & server are implemented. 94% of Node.js's test suite passes. The maxDeflateDynamicTableSize, peerMaxConcurrentStreams and streamResetBurst/streamResetRate options are accepted but ignored.

node:module#

🟑 Missing Module#load(), registerHooks, findPackageJSON, stripTypeScriptTypes, getSourceMapsSupport/setSourceMapsSupport. Overriding require.cache, require.extensions and module._resolveFilename is supported. syncBuiltinESMExports, module._load, module._pathCache and module.register are no-ops (we recommend Bun.plugin instead). findSourceMap always returns undefined.

node:net#

🟒 Fully implemented, including BlockList, SocketAddress, autoSelectFamily, Unix domain sockets and server.listen({ fd }). new net.Socket({ fd }) cannot read from an existing file descriptor (only write-only wrapping works). server.listen(handle) only accepts { fd }. Missing blockList.toJSON()/fromJSON().

node:perf_hooks#

🟑 monitorEventLoopDelay(), createHistogram(), timerify() and PerformanceObserver (mark, measure, function, net, http and http2 entries) are implemented. Bun never emits gc, dns or