Skip to main content
The child_process module is stable and provides the ability to spawn subprocesses similar to popen(3).

Overview

The node:child_process module enables spawning child processes to run shell commands, execute files, and create new Node.js processes.
import { spawn, exec, fork } from 'node:child_process';
// or
const { spawn, exec, fork } = require('node:child_process');

Key Functions

Four main methods for creating child processes:
MethodUse CaseSpawns Shell
spawn()Streaming I/O, long-running processesOptional
exec()Buffered output, shell commandsYes
execFile()Buffered output, direct executionNo (default)
fork()New Node.js processes with IPCNo

Asynchronous Methods

spawn(command[, args][, options])

Spawns a new process with streaming I/O.
command
string
required
The command to run
args
array
List of string arguments
options
object
Configuration options
cwd
string | URL
Current working directory. Default: process.cwd()
env
object
Environment key-value pairs. Default: process.env
argv0
string
Explicitly set argv[0] sent to child process
stdio
array | string
Child’s stdio configuration. Default: 'pipe'
detached
boolean
Prepare child to run independently of parent
uid
number
Sets the user identity of the process
gid
number
Sets the group identity of the process
shell
boolean | string
default:"false"
Run command in a shell. Uses /bin/sh on Unix, process.env.ComSpec on Windows
windowsHide
boolean
default:"false"
Hide subprocess console window on Windows
signal
AbortSignal
AbortSignal to kill the child process
timeout
number
Maximum time in milliseconds the process is allowed to run
killSignal
string | integer
default:"SIGTERM"
Signal to use when killing the process
return
ChildProcess
ChildProcess instance
import { spawn } from 'node:child_process';
import { once } from 'node:events';

const ls = spawn('ls', ['-lh', '/usr']);

ls.stdout.on('data', (data) => {
  console.log(`stdout: ${data}`);
});

ls.stderr.on('data', (data) => {
  console.error(`stderr: ${data}`);
});

const [code] = await once(ls, 'close');
console.log(`child process exited with code ${code}`);

exec(command[, options][, callback])

Spawns a shell and executes a command, buffering output.
command
string
required
The command to run, with space-separated arguments
options
object
Configuration options (extends spawn options)
encoding
string
default:"utf8"
Character encoding for stdout/stderr
timeout
number
default:"0"
Maximum execution time in milliseconds
maxBuffer
number
default:"1024 * 1024"
Largest amount of data in bytes allowed on stdout or stderr
killSignal
string | integer
default:"SIGTERM"
Signal to send when timeout is exceeded
shell
string
Shell to execute the command with. Default: /bin/sh on Unix, process.env.ComSpec on Windows
callback
function
Called when process terminates
  • error
  • stdout
  • stderr
return
ChildProcess
ChildProcess instance
Never pass unsanitized user input to exec(). Any input containing shell metacharacters may trigger arbitrary command execution.
import { exec } from 'node:child_process';

exec('cat *.js missing_file | wc -l', (error, stdout, stderr) => {
  if (error) {
    console.error(`exec error: ${error}`);
    return;
  }
  console.log(`stdout: ${stdout}`);
  console.error(`stderr: ${stderr}`);
});

Promisified Version

import { promisify } from 'node:util';
import child_process from 'node:child_process';
const exec = promisify(child_process.exec);

async function lsExample() {
  const { stdout, stderr } = await exec('ls');
  console.log('stdout:', stdout);
  console.error('stderr:', stderr);
}
lsExample();

execFile(file[, args][, options][, callback])

Similar to exec() but doesn’t spawn a shell by default (more efficient).
file
string
required
The name or path of the executable file to run
args
array
List of string arguments
options
object
Same as exec() options, plus:
windowsVerbatimArguments
boolean
default:"false"
No quoting or escaping of arguments on Windows
return
ChildProcess
ChildProcess instance
import { execFile } from 'node:child_process';

const child = execFile('node', ['--version'], (error, stdout, stderr) => {
  if (error) throw error;
  console.log(stdout);
});

fork(modulePath[, args][, options])

Special case of spawn() for spawning new Node.js processes with IPC channel.
modulePath
string | URL
required
The module to run in the child
args
array
List of string arguments
options
object
Configuration options
cwd
string | URL
Current working directory
detached
boolean
Prepare child to run independently
env
object
Environment key-value pairs. Default: process.env
execPath
string
Executable used to create the child process
execArgv
array
List of string arguments passed to the executable. Default: process.execArgv
serialization
string
default:"json"
Serialization type for messages: 'json' or 'advanced'
silent
boolean
default:"false"
If true, stdin, stdout, and stderr are piped to the parent
stdio
array | string
Child’s stdio configuration. Must contain exactly one 'ipc' item
timeout
number
Maximum execution time in milliseconds
return
ChildProcess
ChildProcess instance with additional communication channel
import { fork } from 'node:child_process';

if (process.argv[2] === 'child') {
  setTimeout(() => {
    console.log(`Hello from ${process.argv[2]}!`);
  }, 1000);
} else {
  const controller = new AbortController();
  const { signal } = controller;
  const child = fork(import.meta.url, ['child'], { signal });
  
  child.on('error', (err) => {
    // Called with AbortError if controller aborts
  });
  
  // controller.abort(); // Stops the child process
}

Synchronous Methods

Synchronous methods block the Node.js event loop until the spawned process exits. Use sparingly!

spawnSync(command[, args][, options])

return
object
Object with pid, output, stdout, stderr, status, signal, error

execSync(command[, options])

return
Buffer | string
The stdout from the command
import { execSync } from 'node:child_process';

const stdout = execSync('ls');
console.log(`stdout: ${stdout}`);

execFileSync(file[, args][, options])

return
Buffer | string
The stdout from the command
import { execFileSync } from 'node:child_process';

try {
  const stdout = execFileSync('my-script.sh', ['my-arg'], {
    stdio: 'pipe',
    encoding: 'utf8',
  });
  console.log(stdout);
} catch (err) {
  if (err.code) {
    console.error('Spawning failed:', err.code);
  } else {
    const { stdout, stderr } = err;
    console.error({ stdout, stderr });
  }
}

ChildProcess Class

Instances of ChildProcess represent spawned child processes.

Events

Event: ‘close’

code
number
Exit code if the child exited on its own
signal
string
Signal that terminated the child process
Emitted when the stdio streams of a child process have been closed.

Event: ‘disconnect’

Emitted after calling subprocess.disconnect() or process.disconnect() in the child.

Event: ‘error’

err
Error
required
Emitted when:
  • Process couldn’t be spawned
  • Process couldn’t be killed
  • Sending a message failed

Event: ‘exit’

code
number
Exit code
signal
string
Signal that terminated the process
Emitted after the child process ends.
const { spawn } = require('node:child_process');
const ls = spawn('ls', ['-lh', '/usr']);

ls.on('exit', (code, signal) => {
  if (signal) {
    console.log(`child process terminated due to signal ${signal}`);
  } else {
    console.log(`child process exited with code ${code}`);
  }
});

Event: ‘message’

message
object
required
Parsed JSON object or primitive value
sendHandle
Handle
net.Socket or net.Server object
Triggered when a child process uses process.send().

Event: ‘spawn’

Emitted once the child process has spawned successfully.

Properties

subprocess.channel

channel
object | undefined
Pipe representing the IPC channel to the child

subprocess.connected

connected
boolean
true until subprocess.disconnect() is called

subprocess.exitCode

exitCode
number | null
Exit code of the child process, or null if still running

subprocess.killed

killed
boolean
true after subprocess.kill() successfully sends a signal

subprocess.pid

pid
number | undefined
Process identifier (PID) of the child process

subprocess.signalCode

signalCode
string | null
Signal that terminated the child process

subprocess.spawnargs

spawnargs
array
Full list of command-line arguments the child process was spawned with

subprocess.spawnfile

spawnfile
string
Executable file name of the child process

subprocess.stdin, stdout, stderr

stdin
stream.Writable | null
Writable stream representing the child’s stdin
stdout
stream.Readable | null
Readable stream representing the child’s stdout
stderr
stream.Readable | null
Readable stream representing the child’s stderr

Methods

subprocess.disconnect()

Closes the IPC channel between parent and child.

subprocess.kill([signal])

Sends a signal to the child process.
signal
string | number
default:"SIGTERM"
Signal to send
return
boolean
true if kill succeeded
const { spawn } = require('node:child_process');
const grep = spawn('grep', ['ssh']);

grep.on('close', (code, signal) => {
  console.log(`child process terminated due to ${signal}`);
});

// Send SIGHUP to process
grep.kill('SIGHUP');

subprocess.ref()

Calling ref() on a child process prevents the parent from exiting if the child is the only thing keeping it running.

subprocess.unref()

Calling unref() on a child process allows the parent to exit independently.

subprocess.send(message[, sendHandle][, options][, callback])

Sends a message to the child process when an IPC channel exists.
message
object
required
Message to send
sendHandle
Handle
net.Socket or net.Server object
options
object
Options object
keepOpen
boolean
default:"false"
Value used when passing net.Socket instances
callback
function
Invoked after the message is sent
return
boolean
true if the message was sent
const subprocess = fork('child.js');
const message = { hello: 'world' };

subprocess.send(message, (error) => {
  if (error) console.error(error);
});

stdio Configuration

The stdio option configures pipes between parent and child.

Shorthand Values

  • 'pipe' - ['pipe', 'pipe', 'pipe'] (default)
  • 'overlapped' - ['overlapped', 'overlapped', 'overlapped']
  • 'ignore' - ['ignore', 'ignore', 'ignore']
  • 'inherit' - ['inherit', 'inherit', 'inherit'] or [0, 1, 2]

Array Values

Each index corresponds to an fd in the child (0=stdin, 1=stdout, 2=stderr):
  1. 'pipe' - Create a pipe between child and parent
  2. 'overlapped' - Same as 'pipe' with FILE_FLAG_OVERLAPPED on Windows
  3. 'ipc' - Create an IPC channel for message passing
  4. 'ignore' - Ignore the fd (opens /dev/null)
  5. 'inherit' - Pass through the corresponding stdio stream
  6. - Share a readable or writable stream
  7. - Share a file descriptor
  8. null/undefined - Use default value
const { spawn } = require('node:child_process');

// Child uses parent's stdio
spawn('prg', [], { stdio: 'inherit' });

// Spawn child sharing only stderr
spawn('prg', [], { stdio: ['pipe', 'pipe', process.stderr] });

// Open extra fd=4 for IPC
spawn('prg', [], { stdio: ['pipe', null, null, null, 'pipe'] });

Detached Processes

On Windows

Setting detached: true makes the child process continue running after the parent exits. The child gets its own console window.

On Unix

The child becomes the leader of a new process group and session. May continue running after parent exits regardless of detached.
import { spawn } from 'node:child_process';
import process from 'node:process';

const subprocess = spawn(process.argv[0], ['child_program.js'], {
  detached: true,
  stdio: 'ignore',
});

subprocess.unref();

Complete Examples

Piping Between Processes

import { spawn } from 'node:child_process';

const ps = spawn('ps', ['ax']);
const grep = spawn('grep', ['ssh']);

ps.stdout.on('data', (data) => {
  grep.stdin.write(data);
});

ps.stderr.on('data', (data) => {
  console.error(`ps stderr: ${data}`);
});

ps.on('close', (code) => {
  if (code !== 0) {
    console.log(`ps process exited with code ${code}`);
  }
  grep.stdin.end();
});

grep.stdout.on('data', (data) => {
  console.log(data.toString());
});

grep.on('close', (code) => {
  if (code !== 0) {
    console.log(`grep process exited with code ${code}`);
  }
});

IPC Between Parent and Child

// parent.js
import { fork } from 'node:child_process';

const child = fork('./child.js');

child.on('message', (msg) => {
  console.log('Message from child:', msg);
});

child.send({ hello: 'world' });
// child.js
process.on('message', (msg) => {
  console.log('Message from parent:', msg);
  process.send({ foo: 'bar' });
});

Security Considerations

Command InjectionWhen using shell: true or exec(), never pass unsanitized user input. Shell metacharacters can trigger arbitrary command execution.
// BAD - Vulnerable to command injection
const userInput = req.query.filename;
exec(`cat ${userInput}`, (err, stdout) => { ... });

// GOOD - Use execFile without shell
const userInput = req.query.filename;
execFile('cat', [userInput], (err, stdout) => { ... });

Best Practices

Choose the Right Method
  • spawn() - Streaming I/O, long-running processes
  • exec() - Small output, shell commands
  • execFile() - Small output, direct execution (more secure)
  • fork() - Node.js processes with IPC
Handle Errors ProperlyAlways listen for 'error' events. Child processes can fail to spawn or be killed unexpectedly.
Avoid Synchronous Methods in ProductionexecSync(), execFileSync(), and spawnSync() block the event loop. Use only for scripts, not servers.