Environment variables are the invisible backbone of every Linux system. They configure how programs find libraries, where commands live, what language to use, and how applications connect to databases. Yet many administrators treat them as black magic — copying export commands from Stack Overflow without understanding why they work or where they should go. In this guide, we will demystify environment variables completely: how to set them, where to put them, how to make them persist across sessions and reboots, and critically, how to handle sensitive values like API keys and database passwords securely.
What Are Environment Variables?
An environment variable is a named value that lives in the shell's memory and is inherited by child processes. When you launch a program from the terminal, it receives a copy of all currently defined environment variables. This is how programs know where to find configuration without hardcoding paths or values.
These variables were set when you logged in, and every command you run inherits them. The $PATH variable is perhaps the most important — it tells the shell where to look for executable programs.
Viewing Environment Variables
There are several commands to inspect the current environment.
env command shows only exported environment variables (passed to child processes). The set command shows everything: exported variables, local shell variables, and shell functions. For debugging, env is usually what you want.
Setting Variables: export vs. Plain Assignment
This is the most commonly confused concept. There are two ways to assign a variable, and they behave very differently.
Plain Assignment (Shell-Only)
A plain assignment creates a shell variable. It exists only in the current shell session and is NOT inherited by child processes (programs, scripts, subshells).
export (Environment Variable)
The export command marks the variable for inheritance. Now every program launched from this shell receives it. This is essential when configuring applications that read their settings from the environment.
| Method | Visible in Current Shell | Visible in Child Processes | Survives Logout |
|---|---|---|---|
MY_VAR="value" | Yes | No | No |
export MY_VAR="value" | Yes | Yes | No |
In ~/.bashrc | Yes | Yes | Yes (per user) |
In /etc/environment | Yes | Yes | Yes (system-wide) |
Where to Put Environment Variables
This is where most confusion lives. Linux has multiple configuration files for environment variables, each loaded at different times and for different purposes.
Per-User Configuration Files
~/.bashrc Most Common
Executed for every new interactive, non-login shell. This includes every new terminal tab, every bash subshell, and most SSH sessions (which source it indirectly).
Best for: Aliases, functions, prompt customization, PATH additions, exported variables for interactive use.
~/.profile
Executed for login shells. This runs once when you first log in (SSH, console login). On Ubuntu, .profile sources .bashrc automatically.
Best for: Variables that should be set once at login. Ubuntu default .profile adds $HOME/bin and $HOME/.local/bin to PATH.
~/.bash_profile
If this file exists, bash reads it INSTEAD of ~/.profile for login shells. Most Ubuntu systems do not have this file by default.
Best for: Only if you need bash-specific login behavior that differs from .profile.
~/.bash_logout
Executed when a login shell exits. Rarely used for environment variables, but useful for cleanup tasks (clearing screen, logging).
Best for: Cleanup, not configuration.
/etc/profile → ~/.bash_profile (or ~/.profile if no bash_profile) → ~/.bashrc (sourced by profile). For a non-login interactive shell: only ~/.bashrc. This is why putting everything in ~/.bashrc is usually the safest approach.
System-Wide Configuration
| File | Scope | Format | When Loaded |
|---|---|---|---|
/etc/environment | All users, all sessions | KEY=value (no export) | PAM login |
/etc/profile | All users, login shells | Shell script | Login |
/etc/profile.d/*.sh | All users, login shells | Shell scripts | Sourced by /etc/profile |
/etc/bash.bashrc | All users, interactive bash | Shell script | Every interactive bash |
Practical Examples
/etc/profile.d/ rather than editing /etc/profile directly. Files in profile.d/ are sourced automatically and survive package updates that might overwrite /etc/profile.
The PATH Variable: Deep Dive
PATH is the most critical environment variable. It is a colon-separated list of directories where the shell looks for commands.
When you type nginx, the shell searches these directories left to right until it finds a match. Order matters — earlier directories take priority.
Adding to PATH
.) to your PATH, especially for root. An attacker could place a malicious script named ls or cat in a directory, and you would execute it unknowingly. Always use absolute paths or dedicated bin directories.
Environment Variables for Applications
The .env File Pattern
Modern web applications (Laravel, Django, Node.js, Rails) use .env files to store configuration. These are NOT automatically loaded by the shell — the application reads them using a library like dotenv.
Systemd EnvironmentFile
When running applications as systemd services, use EnvironmentFile to load variables from a file.
The EnvironmentFile directive loads the file and makes all variables available to the service process. The format is simple KEY=value pairs, one per line. The - prefix (e.g., EnvironmentFile=-/etc/myapp/env) means "do not fail if the file does not exist."
Inline Environment for a Single Command
Prefixing a command with KEY=value sets that variable only for the duration of that single command. The current shell is unaffected.
Common System Environment Variables
| Variable | Description | Typical Value |
|---|---|---|
HOME | Current user's home directory | /root or /home/username |
USER | Current username | root |
SHELL | Default shell | /bin/bash |
PATH | Executable search directories | /usr/local/bin:/usr/bin:... |
LANG | System language/locale | en_US.UTF-8 |
LC_ALL | Override all locale settings | C.UTF-8 |
TERM | Terminal type | xterm-256color |
EDITOR | Default text editor | vim or nano |
HISTSIZE | Number of commands in history | 1000 |
TZ | Timezone | UTC or America/New_York |
SSH_CONNECTION | SSH client IP and port | 192.168.1.50 54321 10.0.0.1 22 |
PWD | Present working directory | /opt/myapp |
Locale Variables Explained
Locale variables control language, formatting, and character encoding. Misconfigured locales cause broken characters in logs, incorrect date formats, and slow sorting operations.
LC_ALL=C.UTF-8 for servers that do not need locale-aware formatting. The C locale is faster for operations like sort, grep, and string comparison because it skips complex Unicode collation rules. Use LANG=en_US.UTF-8 if you need proper language-aware behavior.
Securing Sensitive Environment Variables
Environment variables are commonly used to store secrets: database passwords, API keys, JWT secrets, SMTP credentials. Here is how to handle them safely.
File Permissions
Keeping Secrets Out of Git
git filter-branch or BFG Repo-Cleaner to purge it from history.
The .env.example Pattern
Ship a .env.example with placeholder values so new developers know what variables are needed without seeing real secrets.
Unsetting and Modifying Variables
Environment Variables in Scripts
When writing shell scripts, how variables are handled depends on how you run the script.
Executing a Script
Runs in a child process. Variables set in the script do NOT affect the parent shell. The script inherits exported variables from the parent.
Sourcing a Script
Runs in the current shell. Variables set in the script ARE visible in the parent shell afterward. This is how .bashrc works.
source ~/env-project-x.sh. This can set DATABASE_URL, API keys, and PATH modifications that last for the session.
Debugging Environment Issues
When an application cannot find a variable, use these techniques to track down the problem.
echo $MY_VARexport -p | grep MY_VARcat /proc/PID/environ | tr '\0' '\n' | grep MY_VARsystemctl show myapp --property=EnvironmentBest Practices Summary
User? System? Service?
.bashrc, /etc/environment, EnvironmentFile
chmod 600 for secrets
echo, env, /proc/PID/environ
- Use
exportfor variables that child processes need to see - Put per-user variables in
~/.bashrcfor broadest compatibility - Put system-wide variables in
/etc/environmentor/etc/profile.d/ - Use
EnvironmentFilein systemd for service-specific configuration - Never commit
.envfiles to git — use.env.exampleas a template - Set
chmod 600on any file containing secrets - Prefer
.envfiles orEnvironmentFileover hardcodedexportstatements in profiles - Use
/proc/PID/environto debug what a running process actually sees
Environment variables may seem like a basic topic, but getting them right is the difference between a well-configured server and hours of debugging mysterious application failures. Master the concepts in this guide and you will handle configuration issues with confidence across any Linux system.