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:

  • ~/.bashrc — runs for every new interactive non-login shell (most terminal windows).
  • ~/.bash_profile or ~/.profile — runs for login shells (like SSH sessions).

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:

  • set -a tells Bash to automatically export every variable defined from this point forward.
  • source .env reads the file and defines each variable as if you’d typed it directly.
  • set +a turns off the automatic export behavior again, so subsequent variables in your session aren’t exported unless you say so explicitly.

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

  • Never commit .env files containing real secrets to version control. Use a .env.example file with placeholder values instead.
  • Use export only for variables that genuinely need to be inherited by child processes; keep purely local values as plain shell variables.
  • Namespace your custom variables with a clear prefix (e.g., MYAPP_DB_HOST) to avoid colliding with system or third-party tool variables.
  • Keep system-wide variables in /etc/profile.d/ rather than editing /etc/profile or /etc/environment directly, so your changes are isolated and easy to remove.
  • Document required environment variables in your project’s README so new team members know exactly what to set up.

Security Considerations

  • Environment variables are visible to any process running as the same user, and in some cases visible in process listings (ps eww PID on Linux can reveal a process’s environment). Avoid passing highly sensitive secrets as environment variables in shared or multi-tenant systems if you can use a proper secrets manager instead.
  • Be cautious with logging: many applications accidentally dump their entire environment into debug logs, exposing API keys and passwords. Audit your logging configuration if you rely on environment variables for secrets.
  • Restrict file permissions on .env files (chmod 600 .env) so other users on the same system can’t read them.
  • Avoid setting sensitive variables via command history-visible commands (export API_KEY=secret appears in history). Prefer reading secrets from a file with restricted permissions instead.

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

  • Forgetting export, then wondering why a subprocess or script can’t see the variable.
  • Overwriting $PATH entirely instead of appending or prepending to it, which can break your ability to run standard commands.
  • Storing secrets in shell history by typing them directly instead of loading them from a protected .env file.
  • Mixing up ~/.bashrc and ~/.bash_profile, leading to variables that only work in some types of terminal sessions.

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

Total
2
Shares

Leave a Reply

Previous Post
How to Secure Your Bash Scripts

How to Secure Your Bash Scripts

Next Post
How to Run Bash Scripts at Startup

How to Run Bash Scripts at Startup

Related Posts