How to Set Environment Variables in Bash

How to Set Environment Variables in Bash

How to Set Environment Variables in Bash

Environment variables are one of those things that seem trivial at first — just a name and a value — until you’re debugging why a script can’t find an API key, or why a program behaves differently on your machine than on a colleague’s. I’ve spent hours tracking down bugs that came down to a variable being set in one shell session but not another. Understanding exactly how and where to set environment variables in Bash will save you that same headache.

This guide covers everything from the absolute basics to advanced patterns for managing environment variables safely across scripts, sessions, and systems.

What Environment Variables Actually Are

An environment variable is a named value stored in the shell’s environment that any child process inherits when it’s launched. This is different from a regular shell variable, which stays local to the shell it’s defined in unless explicitly exported.

Here’s the distinction in practice:

MY_VAR="hello"
echo $MY_VAR      # works fine, prints "hello"
bash -c 'echo $MY_VAR'   # prints nothing — child shell doesn't see it

Now compare that with an exported variable:

export MY_VAR="hello"
bash -c 'echo $MY_VAR'   # prints "hello" — child shell inherits it

The export keyword is what promotes a plain shell variable into an environment variable that’s passed down to any subprocess.

Setting a Variable for the Current Session

The simplest way to set an environment variable is directly in your terminal:

export API_KEY="abc123xyz"

You can verify it’s set with:

echo $API_KEY

Or list all currently exported environment variables:

printenv

Or check for one specific variable:

printenv API_KEY

This variable will be available for the rest of your current terminal session, and to any program or script launched from it — but it disappears the moment you close that terminal.

Setting a Variable for a Single Command

Sometimes you only want a variable set for one specific command, without affecting the rest of your session. You can do this inline:

API_KEY="abc123xyz" node server.js

This sets API_KEY only in the environment of that one node server.js process. Once the command finishes, the variable is gone — it was never exported to your shell session at all. This pattern is extremely common in scripts and CI pipelines where you want tightly scoped configuration.

Making Variables Persistent Across Sessions

If you want a variable to be available every time you open a new terminal, you need to add it to one of your shell’s startup files.

For Bash, the most common files are:

Add a line like this to ~/.bashrc:

export EDITOR="vim"
export PATH="$HOME/bin:$PATH"

After editing the file, reload it into your current session without restarting the terminal:

source ~/.bashrc

The PATH example above is worth understanding in detail: $HOME/bin:$PATH prepends your personal bin directory to the existing PATH, meaning executables in ~/bin will be found before anything else. This is the standard way to add personal scripts to your command line without touching system-wide configuration.

System-Wide Environment Variables

If you need a variable available to every user on the system, not just yourself, you have a few options:

/etc/environment — a simple file of KEY=value pairs, applied system-wide at login (note: this file does not support the export keyword or shell expansion):

JAVA_HOME=/usr/lib/jvm/java-17-openjdk

/etc/profile.d/*.sh — a directory of shell scripts sourced by /etc/profile for all login shells. This is the preferred method for system-wide variables that need actual shell logic:

# /etc/profile.d/custom_env.sh
export JAVA_HOME="/usr/lib/jvm/java-17-openjdk"
export PATH="$JAVA_HOME/bin:$PATH"

This approach is cleaner than editing /etc/environment directly because it keeps your customizations in their own file, separate from the system defaults, making upgrades and troubleshooting easier.

Using .env Files for Project-Specific Variables

For application development, it’s common to keep environment variables in a .env file rather than exporting them globally. This keeps configuration scoped to a specific project and easy to share (minus secrets) with teammates.

# .env
DB_HOST=localhost
DB_PORT=5432
DB_NAME=myapp

To load these into your current shell session, you can use a small loop:

set -a
source .env
set +a

Here’s what’s happening:

Many tools (like Docker Compose, Node’s dotenv package, and various frameworks) can read .env files natively without needing this manual loading step.

Unsetting Variables

To remove a variable from your environment entirely:

unset API_KEY

After this, echo $API_KEY will print nothing, and any child process will no longer see it in its environment.

Checking Where a Variable Comes From

One of the trickiest debugging scenarios is when a variable has an unexpected value, and you’re not sure which startup file set it. A useful trick is to grep through your shell config files:

grep -rn "API_KEY" ~/.bashrc ~/.bash_profile ~/.profile /etc/environment /etc/profile.d/ 2>/dev/null

This searches all the common locations at once and shows you exactly which file and line defined the variable, so you’re not guessing.

Real-World Use Cases

1. API keys and secrets in development. Storing credentials in a .env file (excluded from version control via .gitignore) keeps sensitive values out of your codebase while still making them easily accessible to your application.

2. Configuring build tools. Variables like NODE_ENV=production or CI=true change the behavior of build tools and test runners, often triggering optimizations or different logging levels.

3. Multi-environment deployments. The same codebase running in staging vs. production often relies entirely on differing environment variables (DATABASE_URL, LOG_LEVEL, etc.) rather than different code.

4. Customizing your shell. Setting EDITOR, PAGER, or HISTSIZE in your .bashrc tailors your day-to-day terminal experience without needing to remember flags every time.

Best Practices

Security Considerations

Troubleshooting Common Issues

Variable is set but the program can’t see it: Confirm you used export, not just a plain assignment. Plain variables don’t propagate to child processes.

Variable works in one terminal but not another: Check whether you’re comparing a login shell versus a non-login shell — they read different startup files (~/.bash_profile vs ~/.bashrc).

Changes to .bashrc don’t take effect: Remember to source ~/.bashrc or open a new terminal; editing the file alone doesn’t affect already-running shells.

Variable has an unexpected old value: Something earlier in your shell startup chain (or a previous export in the same session) may be setting it before your intended value takes effect. Use grep -rn across your config files as shown above to track it down.

Common Mistakes to Avoid

FAQs

Q: What’s the difference between a shell variable and an environment variable? A shell variable is local to the current shell. An environment variable is exported and inherited by any child processes launched from that shell.

Q: Where should I put environment variables that only apply to one project? Use a .env file in the project root, loaded via source with set -a/set +a, or via your framework’s built-in dotenv support.

Q: How do I make an environment variable available system-wide for all users? Add it to /etc/environment for simple key-value pairs, or create a script in /etc/profile.d/ for anything requiring shell logic.

Q: Can I see all environment variables currently set? Yes, run printenv or env with no arguments.

Q: Why does my exported variable disappear after I close the terminal? Environment variables set directly in a terminal session are temporary. To persist them, add the export line to ~/.bashrc or the appropriate startup file.

Summary

Setting environment variables in Bash is simple on the surface — export NAME=value — but the details around persistence, scope, and security matter a lot in real projects. Whether you’re configuring a single command, your personal shell, or a system-wide setting for every user, choosing the right file and understanding the difference between local and exported variables will keep your systems predictable and your secrets safer.

References

Exit mobile version