For convenience, the exited property is a Promise that resolves when the process exits.
index.ts
const proc = Bun.spawn(["bun", "--version"]);await proc.exited; // resolves when process exitproc.killed; // boolean — was the process killed?proc.exitCode; // null | numberproc.signalCode; // null | "SIGABRT" | "SIGALRM" | ...
To kill a process:
index.ts
const proc = Bun.spawn(["bun", "--version"]);proc.kill();proc.killed; // trueproc.kill(15); // specify a signal codeproc.kill("SIGTERM"); // specify a signal name
The parent bun process will not terminate until all child processes have exited. Use proc.unref() to detach the child process from the parent.
You can set a timeout for a subprocess to automatically terminate after a specific duration:
index.ts
// Kill the process after 5 secondsconst proc = Bun.spawn({ cmd: ["sleep", "10"], timeout: 5000, // 5 seconds in milliseconds});await proc.exited; // Will resolve after 5 seconds
By default, timed-out processes are killed with the SIGTERM signal. You can specify a different signal with the killSignal option:
index.ts
// Kill the process with SIGKILL after 5 secondsconst proc = Bun.spawn({ cmd: ["sleep", "10"], timeout: 5000, killSignal: "SIGKILL", // Can be string name or signal number});
The killSignal option also controls which signal is sent when an AbortSignal is aborted.
For spawnSync, you can limit the maximum number of bytes of output before the process is killed:
index.ts
// Kill 'yes' after it emits over 100 bytes of outputconst result = Bun.spawnSync({ cmd: ["yes"], // or ["bun", "exec", "yes"] on Windows maxBuffer: 100,});// process exits
Bun supports direct inter-process communication channel between two bun processes. To receive messages from a spawned Bun subprocess, specify an ipc handler.
parent.ts
const child = Bun.spawn(["bun", "child.ts"], { ipc(message) { /** * The message received from the sub process **/ },});
The parent process can send messages to the subprocess using the .send() method on the returned Subprocess instance. A reference to the sending subprocess is also available as the second argument in the ipc handler.
parent.ts
const childProc = Bun.spawn(["bun", "child.ts"], { ipc(message, childProc) { /** * The message received from the sub process **/ childProc.send("Respond to child"); },});childProc.send("I am your father"); // The parent can send messages to the child as well
Meanwhile the child process can send messages to its parent using with process.send() and receive messages with process.on("message"). This is the same API used for child_process.fork() in Node.js.
child.ts
process.send("Hello from child as string");process.send({ message: "Hello from child as object" });process.on("message", message => { // print message from parent console.log(message);});
child.ts
// send a stringprocess.send("Hello from child as string");// send an objectprocess.send({ message: "Hello from child as object" });
The serialization option controls the underlying communication format between the two processes:
advanced: (default) Messages are serialized using the JSC serialize API, which supports cloning everything structuredClone supports. This does not support transferring ownership of objects.
json: Messages are serialized using JSON.stringify and JSON.parse, which does not support as many object types as advanced does.
To disconnect the IPC channel from the parent process, call:
To use IPC between a bun process and a Node.js process, set serialization: "json" in Bun.spawn. This is because Node.js and Bun use different JavaScript engines with different object serialization formats.
For interactive terminal applications, you can spawn a subprocess with a pseudo-terminal (PTY) attached using the terminal option. This makes the subprocess think it’s running in a real terminal, enabling features like colored output, cursor movement, and interactive prompts.
const proc = Bun.spawn(["bash"], { terminal: { cols: 80, rows: 24, data(terminal, data) { // Called when data is received from the terminal process.stdout.write(data); }, },});// Write to the terminalproc.terminal.write("echo hello\n");// Wait for the process to exitawait proc.exited;// Close the terminalproc.terminal.close();
When the terminal option is provided:
The subprocess sees process.stdout.isTTY as true
stdin, stdout, and stderr are all connected to the terminal
proc.stdin, proc.stdout, and proc.stderr return null — use the terminal instead
Terminal type for PTY configuration (set TERM env var separately via env option)
"xterm-256color"
data
Callback when data is received (terminal, data) => void
—
exit
Callback when PTY stream closes (EOF or error). exitCode is PTY lifecycle status (0=EOF, 1=error), not subprocess exit code. Use proc.exited for process exit.
—
drain
Callback when ready for more data (terminal) => void
The Terminal object returned by proc.terminal has the following methods:
// Write data to the terminalproc.terminal.write("echo hello\n");// Resize the terminalproc.terminal.resize(120, 40);// Set raw mode (disable line buffering and echo)proc.terminal.setRawMode(true);// Keep event loop alive while terminal is openproc.terminal.ref();proc.terminal.unref();// Close the terminalproc.terminal.close();
Bun.Terminal uses openpty() on Linux and macOS, and ConPTY (CreatePseudoConsole) on Windows. The core behaviour — child sees a TTY, write() reaches the child’s stdin, child output reaches the data callback, resize() updates the child’s view — is the same on every platform. A few details differ:
No termios on Windows.inputFlags, outputFlags, localFlags, and controlFlags always read as 0 and setting them is a no-op. setRawMode() records the flag but has no effect on the child; the child controls its own console mode.
No echo without a child process on Windows. On POSIX, the kernel line discipline echoes write() input back to the data callback even with no process attached. ConPTY has no line discipline; input is buffered for the next reader. If you need echo, spawn a process that echoes.
ConPTY re-encodes output. ConPTY renders the child’s output to a virtual screen and emits whatever VT sequences describe the result, so the data callback receives semantically equivalent — but not byte-identical — escape sequences. Colours and text are preserved; cursor-positioning and reset sequences may be reordered or coalesced. ConPTY also emits a short VT init sequence (\x1b[?9001h\x1b[?1004h…) before any child output.
Input \r is not translated to \n on Windows. POSIX ICRNL maps carriage return to newline on input; ConPTY passes \r through unchanged.
process.on('SIGWINCH') in the child does not fire under ConPTY unless the child is reading stdin in raw mode. process.stdout.columns/rows do update after resize(). This is a libuv limitation that affects any libuv-based child (Node.js included).
On Windows before 11 24H2 (build 26100), terminal.close() may not terminate a still-running child promptly because ClosePseudoConsole blocks until conhost has flushed its output through the pipe on those versions. Kill the attached process first if you need to tear down with a running child.
Bun provides a synchronous equivalent of Bun.spawn called Bun.spawnSync. This is a blocking API that supports the same inputs and parameters as Bun.spawn. It returns a SyncSubprocess object, which differs from Subprocess in a few ways.
It contains a success property that indicates whether the process exited with a zero exit code.
The stdout and stderr properties are instances of Buffer instead of ReadableStream.
There is no stdin property. Use Bun.spawn to incrementally write to the subprocess’s input stream.
⚡️ Under the hood, Bun.spawn and Bun.spawnSync use
posix_spawn(3).
Bun’s spawnSync spawns processes 60% faster than the Node.js child_process module.
terminal
bun spawn.mjs
cpu: Apple M1 Maxruntime: bun 1.x (arm64-darwin)benchmark time (avg) (min … max) p75 p99 p995--------------------------------------------------------- -----------------------------spawnSync echo hi 888.14 µs/iter (821.83 µs … 1.2 ms) 905.92 µs 1 ms 1.03 ms
terminal
node spawn.node.mjs
cpu: Apple M1 Maxruntime: node v18.9.1 (arm64-darwin)benchmark time (avg) (min … max) p75 p99 p995--------------------------------------------------------- -----------------------------spawnSync echo hi 1.47 ms/iter (1.14 ms … 2.64 ms) 1.57 ms 2.37 ms 2.52 ms
A reference of the Spawn API and types are shown below. The real types have complex generics to strongly type the Subprocess streams with the options passed to Bun.spawn and Bun.spawnSync. For full details, find these types as defined bun.d.ts.