Environment Variables & PATH
An environment variable is a named value that the shell (the program that runs your commands) and other programs can read while they run. Think of it as a sticky note that says “DATABASE_URL is this” or “my editor is nano” that any program can look at. Environment variables are how Linux passes settings and secrets to programs without hard-coding them, and the most important one of all, PATH, is what lets you type git instead of /usr/bin/git. Getting comfortable with them is essential for DevOps, because deploying apps, configuring services, and running scripts all depend on the right variables being set.
What an environment variable looks like
A variable has a name (by convention written in UPPERCASE) and a value (always text). Programs read these values to decide how to behave. For example, the LANG variable tells programs your language and character set, and HOME points to your home directory.
To read a single variable, use echo with a $ in front of the name. The $ tells the shell “substitute the value of this variable here.”
echo $HOME
echo $USER
echo $SHELL
Output:
/home/devops
devops
/bin/bash
To see all environment variables currently set, use the env command (or printenv).
env | sort | head -8
Output:
HOME=/home/devops
LANG=en_US.UTF-8
LOGNAME=devops
PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
PWD=/home/devops
SHELL=/bin/bash
TERM=xterm-256color
USER=devops
Tip: Curly braces make the variable name unambiguous.
echo "${USER}_backup"printsdevops_backup, butecho "$USER_backup"looks for a variable namedUSER_backup(which is empty). Use${VAR}whenever the variable is touching other text.
Setting a variable: session vs. persistent
There are two completely different scopes for a variable, and mixing them up is the most common beginner mistake.
| Scope | How you set it | How long it lasts | When to use |
|---|---|---|---|
| Shell-only (local) | NAME=value | Until you close the terminal; only this shell sees it | Quick, throwaway values used in the current session |
| Session (exported) | export NAME=value | Until you close the terminal; child programs see it too | Passing a value to a command or script you’re about to run |
| Persistent (per user) | Add export to ~/.bashrc | Every new login shell for that user | Your personal defaults (editor, PATH additions) |
| Persistent (all users) | Add to /etc/environment | Every login for every user | System-wide settings on a server |
Local vs. exported in the same session
A plain assignment is only visible to the shell itself. To make it visible to programs the shell launches (its “child processes”), you must export it.
MYVAR=hello
echo $MYVAR
bash -c 'echo "child sees: $MYVAR"'
export MYVAR
bash -c 'echo "child sees: $MYVAR"'
Output:
hello
child sees:
child sees: hello
Notice the first child saw nothing because MYVAR was not exported. After export, the child process inherited it. This matters when running scripts or starting services: if your app reads a variable but you forgot to export it, the app sees an empty value.
To remove a variable, use unset:
unset MYVAR
Making variables persistent
Session variables vanish when you log out. To keep them, write them into a startup file that the shell reads automatically.
Per-user: ~/.bashrc
The file ~/.bashrc runs every time you open an interactive Bash shell. Edit it with a text editor:
nano ~/.bashrc
Add a line at the bottom:
export EDITOR=nano
export PROJECT_HOME="$HOME/projects"
Save and exit. The change does not apply to your current shell until you re-read the file (or open a new terminal):
source ~/.bashrc
echo $EDITOR
Output:
nano
source (or its shorthand .) runs the file in your current shell so the new variables take effect immediately, without logging out.
System-wide: /etc/environment
/etc/environment sets variables for every user on the machine. It is read at login by the system, before any shell starts. Note it is not a shell script: you write plain KEY=value lines with no export keyword and no shell syntax.
sudo nano /etc/environment
JAVA_HOME=/usr/lib/jvm/java-21-openjdk-amd64
APP_ENV=production
These apply to new logins. Run source /etc/environment to load them into your current session, or log out and back in.
Security gotcha: Never put secrets (passwords, API keys, database credentials) in
~/.bashrcor/etc/environmenton a shared server. They are readable by processes and can leak into logs orenvdumps. For app secrets, use a dedicated secrets manager or a tightly permissioned.envfile (chmod 600) loaded only by the app.
Understanding PATH
PATH is the single most important environment variable. It is a list of directories, separated by colons, that the shell searches when you type a command name. When you type git, the shell does not scan your whole disk. It walks through each directory in PATH, left to right, and runs the first git it finds.
echo $PATH
Output:
/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
To find out which file actually runs for a command name, use which:
which python3
which ls
Output:
/usr/bin/python3
/usr/bin/ls
This is why a freshly installed program sometimes gives “command not found”: its directory is not in PATH. The fix is to add that directory.
Adding a directory to PATH
Suppose you installed a tool into ~/.local/bin (a common location for user-installed binaries). Add it to PATH by prepending it, keeping the existing PATH on the end:
export PATH="$HOME/.local/bin:$PATH"
Putting your directory first means your version wins if a command exists in two places. To make this permanent, add the same line to ~/.bashrc and source it. Verify the change:
echo $PATH | tr ':' '\n' | head -3
Output:
/home/devops/.local/bin
/usr/local/sbin
/usr/local/bin
Gotcha: Always include
:$PATHwhen extendingPATH. If you writeexport PATH="$HOME/.local/bin"you wipe out every existing directory, and suddenly evenlsandsudostop working in that shell. If that happens, just close the terminal and open a new one.
Best Practices
- Use
exportwhenever a program or script needs to read the variable; a plain assignment stays inside the shell only. - Keep personal defaults in
~/.bashrcand machine-wide settings in/etc/environment; do not duplicate the same variable in both. - Always extend
PATHwithexport PATH="new/dir:$PATH"so you never erase the existing directories. - Never store secrets in shell startup files on shared servers; use a secrets manager or a
chmod 600.envfile. - After editing a startup file, run
source ~/.bashrcto apply changes immediately instead of opening a new terminal. - Use UPPERCASE names for environment variables and
${VAR}braces when the name sits next to other text. - Use
which <command>to debug “command not found” errors, then add the missing directory toPATH.