Skip to content

Home / Glossary / Environment Variable

Tech & IT

Environment Variable

By Sitraka Forler · Lecturer, Durham Business SchoolUpdated 3 October 2026 About this site

A named value set outside your code that a running program reads as configuration.

Also known as: environment variables, env var, env vars.

A process normally inherits NAME=value pairs from whatever started it, and Python reads them through os.environ. Teams keep settings and secrets such as DATABASE_URL there, so one build runs unchanged in development, staging and production. A .env file is not read by the operating system: a tool or library such as python-dotenv loads it, and a changed variable only reaches processes started afterwards.

The intuition

Every process on an operating system starts with a small dictionary of names and values inherited from whatever launched it. Those are its environment variables. PATH tells the shell where to find programs, HOME says where your files live, and anything you add, such as DATABASE_URL or API_KEY, is passed along to every program you start from that shell. The program does not care who set them. That indifference is the point: the same code reads os.environ["DATABASE_URL"] on a laptop, in a test pipeline and in production, and gets a different answer in each.

This is how teams separate configuration from code. Code is committed to Git and is identical everywhere; configuration is injected at run time by the environment, so a database address, a feature flag or a credential never needs to appear in a source file. Secrets in particular belong here: a password typed into a script ends up in Git history, in code reviews and in every clone, whereas a password in an environment variable exists only in the memory of the process that needs it and in the vault or platform setting that supplied it.

The mechanics trip people up in two predictable ways. First, a .env file is just a text file: the operating system ignores it, and only a tool such as python-dotenv, Docker Compose or a platform's build step reads it into the environment. Second, variables are inherited at start-up, so a change made in one terminal is invisible to a program started earlier or from another window. The rule for .env files follows from what they contain: they hold the secrets, so they are listed in .gitignore and never committed, with a .env.example of placeholder names committed instead.

Example

# bash or zsh: set a variable for this shell and the programs it starts
export APP_ENV=staging
python3 -c "import os; print(os.environ.get('APP_ENV', 'development'))"
# prints: staging
unset APP_ENV
python3 -c "import os; print(os.environ.get('APP_ENV', 'development'))"
# prints: development

Moving a market-data API key out of a script and into the environment, on a laptop and in production

  1. The starting point is a script fetch_prices.py that contains API_KEY = "sk_live_..." on line 3. It works, and the key is now in the Git history of a repository shared with twelve people. The first step is to revoke that key with the provider and issue a new one; removing the line does not remove it from history.
  2. Change the code to read the environment: import os, then API_KEY = os.environ["MARKET_DATA_API_KEY"]. Using os.environ[...] rather than os.environ.get(...) means a missing variable raises KeyError at start-up instead of sending an empty key to the provider and failing later in a confusing way.
  3. Set it for one shell session: export MARKET_DATA_API_KEY=sk_live_new... then python3 fetch_prices.py. The script runs. Open a second terminal window and run it again: KeyError, because the second shell never saw the export. Variables are inherited at start-up, not shared globally.
  4. Make it convenient on the laptop with a .env file: one line, MARKET_DATA_API_KEY=sk_live_new..., and in the script from dotenv import load_dotenv; load_dotenv() before reading os.environ. Add .env to .gitignore immediately, and commit a .env.example containing MARKET_DATA_API_KEY= with no value so colleagues know what to set.
  5. In production the .env file does not exist. The scheduler that runs the nightly job reads the key from the firm's vault and injects it as an environment variable when it starts the process; on a platform such as Vercel or Heroku, it is entered once in the project's settings. The code is unchanged: load_dotenv() finds no file and does nothing, and os.environ holds the value.
  6. What goes wrong: a colleague runs git add . and commits the .env file because .gitignore was edited after the file was created and already staged. The key is revoked again, the commit is rewritten, and a pre-commit hook that blocks files named .env is added. Rotation is cheap; a leaked key is not.

Configuration lives in the environment and code only reads it: export a variable or load a .env file on your laptop, let the vault or platform inject it in production, and never let the .env file reach Git.

Common pitfalls

  • Committing a .env file: the secrets are then in Git history for everyone with a clone, and deleting the file in a later commit does not remove them.
  • Expecting a variable set in one terminal to appear in another window, in an IDE launched earlier or in a cron job: each process inherits its environment at start-up.
  • Using os.environ.get with a silent default for a secret: the job runs with an empty key and fails somewhere far from the cause.
  • Printing the environment in logs for debugging: os.environ contains every secret the process has, and logs are read by more people than the vault.
  • Mixing environments by accident: a laptop .env left pointing at the production database turns a quick local test into a production write.

Frequently asked questions

What is an environment variable in simple terms?

A named value, such as DATABASE_URL, that the operating system hands to a program when it starts. Programs read it as configuration instead of hard-coding the value, so the same code can run against a local database on a laptop and the real one in production. In Python you read it with os.environ["NAME"]; in bash you set it with export NAME=value.

How do I set an environment variable?

In bash or zsh, export NAME=value sets it for the current shell and everything started from it; in PowerShell, $env:NAME = "value". For persistence on a laptop, put the line in a .env file loaded by python-dotenv, or in your shell profile. On servers and platforms, set it in the service's configuration, a vault, the systemd unit or the Heroku and Vercel settings page.

Why should I use environment variables for secrets?

Because a secret in code ends up in Git history, in every clone and in code reviews, and cannot be rotated without a code change. An environment variable keeps the secret out of the repository, lets each environment have its own value, and is what vaults and deployment platforms are built to inject. Keep .env files out of Git and commit a .env.example with empty values instead.

Does a .env file set environment variables automatically?

No. The operating system does not read .env files. A library such as python-dotenv, a tool such as Docker Compose, or a platform's build step parses the file and copies its lines into the process environment. If you forget to call load_dotenv(), or run the program from a directory where the file is not found, the variables are simply absent.

Test yourself

Practise Environment Variable hands-on - free, in your browser

Related terms