Open-Source Wikis

/

Node.js

/

Systems

/

Bootstrap and startup

nodejs/node

Bootstrap and startup

This page traces what happens between int main() and the first line of app.js running. Owners: @nodejs/startup (per .github/CODEOWNERS).

Purpose

Boot Node.js: parse arguments, initialize V8, OpenSSL, ICU, and libuv, build the V8 isolate / context / Realm, run a JS bootstrap that sets up process and the global object, then dispatch to a "main script" appropriate for the invocation mode (file, REPL, eval, worker, test runner, etc.).

Directory layout

src/
  node_main.cc                   // platform main(); calls node::Start
  node.cc                        // node::Start, InitializeOncePerProcess, Bootstrap
  node_main_instance.{cc,h}      // NodeMainInstance: per-isolate driver
  node_realm.{cc,h}              // Realm and PrincipalRealm
  node_snapshotable.{cc,h}       // SnapshotBuilder + SnapshotData
  node_snapshot_builder.h        // ABI for embedders
  node_v8_platform-inl.h         // V8 platform shim
  node_options.{cc,h}            // CLI flag definitions
  node_dotenv.{cc,h}             // .env loader
  node_config_file.{cc,h}        // node-config-schema.json reader
  node_task_runner.{cc,h}        // npm-style script runner
  node_builtins.{cc,h}           // BuiltinLoader: compiles lib/**/*.js
  node_revert.h                  // --security-revert flags
  node_credentials.cc            // POSIX uid/gid setup
lib/
  internal/per_context/          // per-Realm per-Context JS init
    primordials.js               // captures JS builtins into `primordials`
    domexception.js              // DOMException
    messageport.js               // MessagePort glue
  internal/bootstrap/
    realm.js                     // sets up internalBinding, BuiltinModule
    node.js                      // top-level bootstrap; sets up process, globals
    shadow_realm.js              // ShadowRealm boot
    web/                         // exposeLazy + Web API setup
    switches/                    // optional bootstrap (deprecation warnings,
                                 //   freeze intrinsics, etc.)
  internal/process/
    pre_execution.js             // post-snapshot, pre-main setup
    per_thread.js                // bootstrap shared by main + workers
    execution.js                 // runMain, executeUserEntryPoint
    finalization.js              // process.finalization
    promises.js                  // unhandledRejection plumbing
    task_queues.js               // microtask + nextTick
    warning.js                   // process.emitWarning
    permission.js                // permission model wiring
    signal.js                    // signal handler bridge
  internal/main/
    run_main_module.js           // node app.js
    eval_string.js               // node -e
    eval_stdin.js                // node piped stdin
    repl.js                      // interactive REPL
    check_syntax.js              // node --check
    inspect.js                   // node inspect
    print_help.js                // --help
    test_runner.js               // node --test
    watch_mode.js                // node --watch
    worker_thread.js             // worker bootstrap
    embedding.js                 // embedder entry
    mksnapshot.js                // snapshot generator
    prof_process.js              // --prof-process

Key abstractions

Type / file Role
node::Start (src/node.cc) Top-level entry. Calls InitializeOncePerProcess, builds NodeMainInstance, runs.
node::InitializeOncePerProcess (src/node.cc) Init V8 platform, OpenSSL, ICU, dotenv, large-pages, parses CLI flags.
node::NodeMainInstance (src/node_main_instance.cc) Owns the V8 isolate and the principal Environment/Realm.
node::Environment (src/env.cc) Per-thread state: libuv loop, options, inspector, performance state.
node::Realm (src/node_realm.cc) Per-V8-context JS state: bindings, builtin loader, process.
node::SnapshotBuilder::Generate (src/node_snapshotable.cc) Builds the embedded V8 startup snapshot at build time.
node::builtins::BuiltinLoader (src/node_builtins.cc) Loads lib/**/*.js from the binary; backs internalBinding JS-side resolver.
lib/internal/bootstrap/realm.js Creates internalBinding, BuiltinModule, process.binding allow list.
lib/internal/bootstrap/node.js Sets up process, the global object, timers, async hooks dispatch.
lib/internal/process/pre_execution.js Runs after snapshot deserialization; reads env/argv, installs inspector, runs --require.
lib/internal/main/*.js Selected based on argv/flags; each is a tiny bootstrapper for a mode.

How it works

sequenceDiagram
    participant OS
    participant nm as node_main.cc
    participant Start as node::Start
    participant InitOnce as InitializeOncePerProcess
    participant Inst as NodeMainInstance::Run
    participant Realm as Realm::BootstrapRealm
    participant RJ as bootstrap/realm.js
    participant NJ as bootstrap/node.js
    participant PE as process/pre_execution.js
    participant Main as main/<entry>.js

    OS->>nm: argc, argv, envp
    nm->>Start: node::Start
    Start->>InitOnce: parse argv, init V8/OpenSSL/ICU
    InitOnce-->>Start: shared platform ready
    Start->>Inst: NodeMainInstance::Run
    Inst->>Realm: enter principal Realm

    alt Snapshot enabled (default)
        Realm-->>Realm: deserialize V8 heap from embedded snapshot
        Realm->>NJ: continue from post-bootstrap state
    else --no-node-snapshot
        Realm->>RJ: compile + run bootstrap/realm.js
        RJ->>NJ: compile + run bootstrap/node.js
    end

    NJ->>PE: setupPreExecution(...)
    PE-->>PE: parse env, set up inspector, --require
    PE->>Main: choose main script
    Main-->>OS: run user code, drive event loop

The snapshot path matters because most of the code in lib/internal/bootstrap/realm.js and lib/internal/bootstrap/node.js runs at build time when generating the snapshot. The deserialized heap already has process, the global object, the timer machinery, and the async-hook dispatch wired up.

Realm::BootstrapRealm

src/node_realm.cc is where C++ → JS handover happens:

  1. Loads lib/internal/per_context/primordials.js and friends into the V8 context.
  2. Compiles internal/bootstrap/realm.js via BuiltinLoader::CompileAndCall. That installs internalBinding, process.binding, and the BuiltinModule registry on the realm.
  3. Compiles internal/bootstrap/node.js (the "principal" path; a parallel path for ShadowRealms goes to bootstrap/shadow_realm.js).
  4. Calls into bootstrap/switches/ based on CLI flags (e.g. --frozen-intrinsics).
  5. Hands control back to C++, which calls setupPreExecution and then loadEnvironment, which dispatches to one of lib/internal/main/*.js.

pre_execution.js

Anything that depends on runtime state lives here, not in bootstrap:

  • Read --inspect, --inspect-brk and start the inspector agent.
  • Read --require modules and run them.
  • Set up the source-map cache (lib/internal/source_map/).
  • Apply --frozen-intrinsics if requested.
  • Honour process.dlopen overrides for the legacy addons API.

This is also the file you edit when adding a new CLI flag that needs an early hook.

Main script selection

src/node.cc (LoadEnvironment and friends) decides which script in lib/internal/main/ to run based on Environment::Options:

Condition Main script
--eval provided lib/internal/main/eval_string.js
--check lib/internal/main/check_syntax.js
--print eval_string.js with print=true
Stdin script (no positional) lib/internal/main/eval_stdin.js
--interactive or no script and stdin is a TTY lib/internal/main/repl.js
--inspect subcommand lib/internal/main/inspect.js
--help lib/internal/main/print_help.js
--test lib/internal/main/test_runner.js
--watch lib/internal/main/watch_mode.js
Inside Worker lib/internal/main/worker_thread.js
Embedder API lib/internal/main/embedding.js
--build-snapshot lib/internal/main/mksnapshot.js
--prof-process lib/internal/main/prof_process.js
Default lib/internal/main/run_main_module.js

Each main script is small (≤ a couple hundred lines) and orchestrates the entry-point resolution + invocation in terms of internal/modules/run_main and friends.

Snapshot pipeline

graph LR
  cfg[node.gyp + tools/snapshot/] --> mks[node_mksnapshot binary]
  perc[lib/internal/per_context/*.js] --> mks
  rjs[lib/internal/bootstrap/realm.js] --> mks
  njs[lib/internal/bootstrap/node.js] --> mks
  mks --> blob[startup snapshot blob]
  blob --> nodebin[node binary]
  nodebin --> deser[Realm deserializes blob]

When a snapshot is present (default for the principal Realm), Realm::DeserializeProperties swaps in the prebuilt heap. --no-node-snapshot forces the slow path. --build-snapshot lets users build their own snapshots via the Snapshot Builder API; the JS side lives in v8.startupSnapshot.* (lib/v8.js).

Integration points

  • CLI flags: defined in src/node_options.cc via addOption(...). Most flags are also exposed to JS through internalBinding('options'). Any new CLI flag goes here.
  • Embedded JS: tools/js2c.cc consumes lib/**/*.js and deps/**/*.js listed in node.gyp and produces node_javascript.cc. The list of bundled files is in node.gyp's library_files variable.
  • process object: assembled in lib/internal/bootstrap/node.js and lib/internal/process/per_thread.js. Worker threads run per_thread.js separately so they get a parallel process shape.
  • dotenv: src/node_dotenv.cc parses --env-file before bootstrap; results are exposed as process.env.
  • Config file: --experimental-default-config-file and --config-file go through src/node_config_file.cc against doc/node-config-schema.json.
  • Task runner: node --run <script> runs npm-style scripts; implementation in src/node_task_runner.cc.

Entry points for modification

To add a CLI flag: edit src/node_options.cc and src/node_options.h; if the flag affects bootstrap-only state, also touch lib/internal/process/pre_execution.js and doc/api/cli.md.

To add a new main script mode: create lib/internal/main/<name>.js, register it in node.gyp's library_files, dispatch from src/node.cc (LoadEnvironment).

To change what gets snapshot-frozen: anything in lib/internal/bootstrap/** and the internal/per_context/** files runs while building the snapshot. Read those files' headers — they list strict rules.

For the JS side of snapshots used by user code, see lib/v8.js startupSnapshot.addDeserializeCallback/addSerializeCallback and src/node_snapshotable.cc.

Built by Factory AutoWiki from public repository content. It is a generated preview for codebase exploration, not source-maintained documentation.

Bootstrap and startup – Node.js wiki | Factory