# Where Are Node.js process.env Properties Defined? Inside the C++ Source Code

> Discover where Node.js process.env properties are defined. Explore the C++ source code src/node_process.cc and src/node_env_var.cc to understand native variable synchronization with libuv.

- Repository: [Node.js/node](https://github.com/nodejs/node)
- Tags: internals
- Published: 2026-02-15

---

**Node.js `process.env` properties are defined in the C++ source files `src/node_process.cc` and `src/node_env_var.cc`, where native getters and setters synchronize the JavaScript object with the underlying operating system environment variables using libuv.**

The `process.env` object provides a JavaScript view of the process’s environment variables. While it appears as a standard object in your Node.js code, its behavior is implemented in the native C++ layer of the `nodejs/node` repository. This implementation handles everything from reading system environment variables to enforcing type safety when setting values.

## Core Implementation in the C++ Layer

The implementation spans two primary files in the `src/` directory. The `process` object itself is instantiated in one file, while the specific accessor logic for the `env` property resides in another.

### The process Object Initialization (src/node_process.cc)

The global `process` object is created in **`src/node_process.cc`**. This file sets up the `process` global available in every Node.js context and registers the `env` accessor. It establishes the connection between the JavaScript runtime and the native environment variable storage, but delegates the actual get/set operations to the specialized environment variable module.

### The env Accessor Logic (src/node_env_var.cc)

The getter and setter logic for `process.env` is implemented in **`src/node_env_var.cc`**. This file contains the core machinery that translates between JavaScript property access and system calls.

- **The getter** constructs a plain JavaScript object that mirrors the current environment. It iterates through the OS environment variables using `uv_os_getenv` (libuv’s cross-platform environment variable getter) and populates the object.
- **The setter** validates that keys and values are strings, disallows defining getters or setters on `process.env`, and updates the underlying OS environment via `uv_os_setenv`. It also contains the deprecation warning logic that alerts users when attempting to assign non-string values.
- **Deletion** is handled by the setter detecting `undefined` values and removing the variable from the environment using the appropriate libuv deletion calls.

## Environment Propagation in Workers and Realms

When Node.js creates new execution contexts, the environment variables must be copied or cloned to maintain isolation while preserving access to the system environment.

- **`src/node_worker.cc`** – When spawning a new Worker thread, this file copies the current `process.env` into the worker’s environment using the same accessor logic found in `node_env_var.cc`.
- **`src/node_realm.cc` and `src/node_shadow_realm.cc`** – These files handle the creation of new realms (including ShadowRealms). They clone the environment object to ensure the new realm has access to the same environment variables, maintaining consistency across different JavaScript contexts within the same process.

## Practical Code Examples

The following examples demonstrate how the C++ implementation manifests in JavaScript behavior:

```javascript
// Reading an environment variable triggers the C++ getter in node_env_var.cc
const home = process.env.HOME;

// Setting a variable invokes the setter, which validates the string type
// and calls uv_os_setenv to update the OS environment
process.env.MY_VAR = '123';

// The setter detects deletion and removes the variable from the environment
delete process.env.MY_VAR;

// In a Worker, the environment is copied from the parent thread
const { Worker } = require('worker_threads');
new Worker(`console.log(process.env.PATH)`, { env: { ...process.env } });

```

## Summary

- **`src/node_process.cc`** creates the `process` global and registers the `env` accessor.
- **`src/node_env_var.cc`** implements the getter and setter logic, using `uv_os_getenv` and `uv_os_setenv` to synchronize with the OS environment.
- The setter enforces string-only values and prevents defining getters or setters on `process.env`.
- **`src/node_worker.cc`**, **`src/node_realm.cc`**, and **`src/node_shadow_realm.cc`** copy the environment to new contexts using the same accessor implementation.

## Frequently Asked Questions

### Where is the process.env getter implemented in the Node.js source code?

The getter is implemented in **`src/node_env_var.cc`**. It constructs a plain JavaScript object by iterating over the operating system environment variables using `uv_os_getenv`, which is libuv’s cross-platform function for reading environment variables.

### Why does process.env only allow string values?

The setter in **`src/node_env_var.cc`** explicitly validates that both keys and values are strings. This restriction exists because environment variables are inherently string-based at the operating system level. The implementation includes deprecation warnings when non-string values are assigned to prevent unexpected type coercion behavior.

### How does Node.js handle process.env in Worker threads?

When a new Worker is created, **`src/node_worker.cc`** copies the current `process.env` object into the worker’s isolated context using the same accessor logic defined in `src/node_env_var.cc`. This ensures workers have access to the parent thread’s environment variables while maintaining proper isolation between contexts.

### Can you define getters or setters on process.env?

No. The implementation in **`src/node_env_var.cc`** explicitly disallows defining getters or setters on the `process.env` object. The setter logic validates incoming property descriptors and rejects attempts to define accessor properties, ensuring that `process.env` remains a simple data object synchronized with the underlying OS environment.