pickuma.
Dev Knowledge

Environment Variables and PATH, Explained

What environment variables actually are, why they hold config and secrets, and how PATH decides which binary runs when you type a command.

5 min read

Type node and the right program runs. Set DATABASE_URL and your app connects without a single credential in the source. Both of those are environment variables doing quiet work, and understanding them removes a whole category of confusing errors.

What an environment variable is

An environment variable is a named string the operating system attaches to a running process. Each process gets its own set, stored as simple KEY=value pairs — there are no numbers, booleans, or nested objects at this level, just text. Your program reads them through whatever its language exposes: process.env.HOME in Node, os.environ["HOME"] in Python, $HOME in a shell.

The detail that makes them useful is inheritance. When a process starts a child process, the child receives a copy of the parent’s environment. Your shell has an environment; every command you launch from it inherits that environment; anything those commands launch inherits it in turn. This is why exporting a variable in your terminal makes it visible to the programs you then run there — and why it vanishes when you close the terminal, since that process and its environment are gone.

A few names show up almost everywhere. HOME points at your home directory. USER (or USERNAME on Windows) holds your login name. SHELL names your default shell, PWD your current directory, and LANG your locale. You rarely set these yourself; the system populates them at login.

You set your own variables in a few standard ways. In a shell, export NAME=value makes the variable available to child processes for that session (plain NAME=value without export keeps it local to the shell itself). To persist it, add the export line to a startup file like ~/.bashrc or ~/.zshrc. For applications, a .env file holds project-specific values that a loader reads at startup. In CI and production, the same values are injected as pipeline secrets or platform-managed environment settings.

Why config and secrets live here

The reason this pattern is everywhere is separation. Your code describes behavior; the environment describes the situation it runs in — which database, which API key, which log level. Keeping those apart means the exact same build runs in development, staging, and production with nothing changed but the environment around it.

This is the idea behind the “config in the environment” rule popularized by the Twelve-Factor App methodology: anything that varies between deploys should live in the environment, not in the code. The practical payoff is concrete. Secrets stay out of version control, so a leaked repository does not leak your production keys. Rotating a credential is an environment change and a restart, not a code change and a redeploy. And there is no risk of a developer’s local database URL accidentally shipping to production, because the URL was never in the code to begin with.

PATH: how the shell finds commands

PATH is the single most consequential environment variable, and it works exactly like the others — it is just a string. Its value is a list of directories joined by colons on macOS and Linux (semicolons on Windows):

$ echo $PATH
/usr/local/bin:/usr/bin:/bin:/opt/homebrew/bin

When you type a bare command like git, the shell does not search your whole disk. It walks the directories in PATH from left to right and runs the first executable named git it finds. If it reaches the end of the list without a match, you get the familiar command not found. That error is rarely about a missing program — usually the binary exists but its directory is not on PATH. (You can ask the shell which file it would actually run with which git or, in many shells, type git.)

To make a new tool runnable, you add its directory to PATH. Prepending puts it ahead of everything else:

export PATH="$HOME/.local/bin:$PATH"

The order matters more than it looks.

FAQ

FAQ

Why does my variable disappear when I open a new terminal?+
An export only affects the current shell process and its children. A new terminal is a fresh process that never inherited it. To make a variable stick, add the export line to a shell startup file like ~/.bashrc or ~/.zshrc, which runs each time a shell starts.
What is the difference between setting NAME=value and export NAME=value?+
Plain NAME=value creates a shell variable visible only inside that shell. export NAME=value marks it for inheritance, so child processes you launch also receive it. Programs you run from the shell only see exported variables, which is why config meant for an app must be exported.
Are .env files automatically loaded by the operating system?+
No. A .env file is just a text file; the OS knows nothing about it. Something has to read it — a library like dotenv, your framework, or a tool like docker compose. Outside that loader, the variables in the file do not exist in any process environment.

Related reading

See all Dev Knowledge articles →

Get the best tools, weekly

One email every Friday. No spam, unsubscribe anytime.