Example: Spawn multiple Bun server processes
```ts
import { spawn } from "bun";
const cpus = navigator.hardwareConcurrency;
const buns = new Array(cpus);
for (let i = 0; i < cpus; i++) {
buns[i] = spawn({
cmd: ["bun", "./server.ts"],
stdout: "inherit",
stderr: "inherit",
stdin: "inherit",
});
}
function kill() {
for (const bun of buns) {
bun.kill();
}
}
process.on("SIGINT", kill);
process.on("exit", kill);
```
This example shows how to spawn multiple Bun server processes equal to the number of CPU cores and manage their lifecycle with signal handlers.
node:worker_threads limitations
node:worker_threads Worker ignores the resourceLimits and trackUnmanagedFds options, and execArgv only sets process.execArgv in the worker. worker.performance.eventLoopUtilization() is a stub. Missing moveMessagePortToContext and locks.
Worker and concurrency APIs in Bun
Bun supports Web Workers, including Worker, self.postMessage, structuredClone, MessagePort, MessageChannel, and BroadcastChannel.
Create a Worker instance
Create a new worker thread using the Worker constructor with a path to a script file. Pass the script path as a string to the Worker constructor. For example: const worker = new Worker("./worker.ts");
Worker postMessage and onmessage
Send messages from main thread to worker using worker.postMessage(). Send messages from worker thread to main thread using postMessage() directly (no prefix needed in worker context). Receive messages on main thread with worker.onmessage event handler or worker.addEventListener('message', ...). Receive messages on worker thread with self.onmessage or self.addEventListener('message', ...).
Worker TypeScript setup
To prevent TypeScript errors when using self in a worker file, add this declaration at the top of the worker script: declare var self: Worker;
Worker preload option
Pass a preload option to the Worker constructor to load modules before the worker's own code runs, similar to the --preload CLI argument. Use it for code that must load first, such as OpenTelemetry, Sentry, or DataDog. The preload option accepts either an array of module specifiers or a single string: new Worker("./worker.ts", { preload: ["./load-sentry.js"] }) or new Worker("./worker.ts", { preload: "./load-sentry.js" }).
Worker from blob URL
Create a worker from a string or in-memory source by passing a blob: URL to the Worker constructor. Create a Blob or File with the worker code, use URL.createObjectURL() to create a blob: URL, then pass it to new Worker(). For TypeScript support, set the type property on the Blob to "application/typescript" or use the File constructor with a .ts filename.
Worker open event
Bun emits an 'open' event when a worker is created and ready to receive messages. This event does not exist in browsers. You do not need to wait for the 'open' event before sending messages; Bun enqueues messages until the worker is ready.
postMessage serialization algorithm
Bun serializes messages sent via postMessage() using the HTML Structured Clone Algorithm. Bun provides fast paths for common data types: string fast path (pure strings bypass structured clone entirely), and simple object fast path (plain objects containing only primitive values, with no prototype chain modifications, no getters/setters, and no indexed properties).
postMessage performance optimizations
With Bun's fast paths, postMessage performs 2-241x faster than Node.js v24.6.0. Benchmark results: postMessage with 11-char string + 9 props: 648ns (Bun) vs 1.19µs (Node.js); with 14 KB string: 719ns (Bun) vs 2.69µs (Node.js); with 3 MB string: 1.26µs (Bun) vs 304µs (Node.js).
Worker terminate method
Call worker.terminate() to forcefully terminate a worker. A Worker instance terminates automatically once its event loop has no work left to do. Attaching a 'message' listener on the global or any MessagePorts keeps the event loop alive.
Worker process.exit()
A worker can terminate itself with process.exit(). This does not terminate the main process. Like in Node.js, process.on('beforeExit', callback) and process.on('exit', callback) are emitted on the worker thread, not on the main thread. Bun passes the exit code to the 'close' event.
Worker close event
Bun emits the 'close' event when a worker has been marked as terminated. The CloseEvent contains the exit code passed to process.exit(), or 0 if it closed for another reason. This event does not exist in browsers.
Worker unref method
Call worker.unref() to stop a running worker from keeping the main process alive. This decouples the worker's lifetime from the main process's, matching the behavior of Node.js' worker_threads. worker.unref() is not available in browsers.
Worker ref method and ref option
Call worker.ref() to keep the process alive until the Worker terminates. Workers are ref'd by default. Alternatively, pass ref: false in the options object to the Worker constructor, which is equivalent to calling worker.unref(): new Worker("./worker.ts", { ref: false }). worker.ref() is not available in browsers.
Worker smol mode for memory reduction
Bun's Worker supports a smol mode that reduces memory usage at a cost of performance. To enable it, pass smol: true in the Worker constructor's options object: new Worker("./i-am-smol.ts", { smol: true }). Setting smol: true sets JSC::HeapSize to be Small instead of the default Large.
setEnvironmentData and getEnvironmentData
Share data between the main thread and workers using setEnvironmentData() and getEnvironmentData() from the worker_threads module. Call setEnvironmentData(key, value) in the main thread to set data, and getEnvironmentData(key) in a worker to retrieve it. Example: setEnvironmentData("config", { apiUrl: "https://api.example.com" }); const config = getEnvironmentData("config");
Worker creation event
Listen for worker creation events using process.on('worker', worker => { ... }). The callback receives the worker object with a threadId property.
Bun.isMainThread check
Use Bun.isMainThread to determine whether code is running on the main thread or in a worker. Returns true if on the main thread, false if in a worker.
Worker script resolution
Bun resolves the script specifier passed to the Worker constructor relative to the project root, similar to typing 'bun ./path/to/file.js'.
Worker error event
If the worker's script fails to resolve, Bun emits an 'error' event on the Worker object. Listen with worker.addEventListener('error', event => { console.log(event.message); }).
Worker supports multiple file types
Like the rest of Bun, Worker supports CommonJS, ES modules, TypeScript, JSX, and TSX with no extra build step. You can use import and export syntax in worker code without needing to pass {type: "module"} like in browsers.
Worker API is experimental
The Worker API in Bun is still experimental, particularly for terminating workers. Bun is actively working on improving it.