LINUX / TUTORIAL

Linux environment variables, explained properly

How to set temporary and permanent environment variables, list them, scope them to users or sessions, and avoid the classic mistakes that break deployments.

· 5 min read

Environment variables are one of the most common ways to pass configuration to Linux programs: database URLs, API keys, PATH extensions, locale settings. But there's a lot of subtle confusion around setting them temporarily versus permanently, exporting them versus not, and which config file to use. This guide clears it up.

Listing existing environment variables

There are three ways to list all environment variables. They return similar results with minor differences:

env          # Most common — shows exported variables
printenv     # Same as env, slightly different formatting
set          # Shows ALL variables including shell functions

To check a specific variable:

echo $PATH
printenv HOME
echo "Database URL is: $DATABASE_URL"

Setting a temporary environment variable

This is the source of most confusion. There are two subtly different ways to set a variable, and they behave differently:

# Method 1: simple assignment (NOT exported)
MYVAR="hello"
echo $MYVAR              # prints: hello
bash -c 'echo $MYVAR'    # prints: (empty — child process can't see it)

This only sets the variable in the current shell. Child processes (like a Python script, a Node app, or a Docker container you launch) won't see it.

# Method 2: export (makes it visible to child processes)
export MYVAR="hello"
bash -c 'echo $MYVAR'    # prints: hello

The rule: if you want a process you launch from this shell to see the variable, you need export.

Setting a variable for one command only

Sometimes you want to set a variable just for a single command, without affecting anything else. Put the assignment in front of the command:

NODE_ENV=production node app.js
DEBUG=express:* npm start
DATABASE_URL=postgres://localhost/test pytest tests/

The variable exists only for that one command. The next command won't see it. This is cleaner than exporting globally and forgetting to unset.

Making a variable permanent (for your user)

To set a variable that persists across shell sessions, add it to one of these files:

  • ~/.bashrc — runs for interactive non-login shells (most common)
  • ~/.profile or ~/.bash_profile — runs for login shells
  • ~/.zshrc — equivalent for Zsh users

Add the export at the end of the file:

echo 'export DATABASE_URL="postgres://localhost/mydb"' >> ~/.bashrc

The variable takes effect in new shells. To load it in your current shell without opening a new terminal:

source ~/.bashrc

System-wide environment variables

For variables that should be available to every user on the system, use /etc/environment (note: this file does NOT support export or shell syntax — just KEY=value):

# /etc/environment
DATABASE_URL="postgres://localhost/production"
APP_ENV="production"
JAVA_HOME="/usr/lib/jvm/default-java"

Alternatively, for shell-specific defaults, edit /etc/profile or create a file in /etc/profile.d/:

# /etc/profile.d/myapp.sh
export MYAPP_HOME="/opt/myapp"
export PATH="$MYAPP_HOME/bin:$PATH"

Unsetting a variable

Remove an environment variable with unset:

unset MYVAR
echo $MYVAR    # prints: (empty)

Setting variables for systemd services

If you're running a service under systemd (Node app, Python worker, etc.), environment variables set in ~/.bashrc won't be picked up because systemd doesn't source your shell configs. You need to set them in the unit file:

[Service]
Environment="NODE_ENV=production"
Environment="DATABASE_URL=postgres://localhost/mydb"
ExecStart=/usr/bin/node /app/server.js

Or load them from a file (cleaner for secrets):

[Service]
EnvironmentFile=/etc/myapp/env
ExecStart=/usr/bin/node /app/server.js

Then /etc/myapp/env contains KEY=value pairs (no export, no quotes needed for simple values).

The PATH variable (special case)

$PATH is a colon-separated list of directories the shell searches for commands. To add a directory:

# Prepend (searched first)
export PATH="/opt/myapp/bin:$PATH"

# Append (searched last)
export PATH="$PATH:/opt/myapp/bin"

Always include $PATH in the assignment. Writing export PATH="/opt/myapp/bin" replaces your entire PATH and breaks your shell (common mistake — you won't be able to run ls or anything).

Common environment variables you should know

  • $HOME — your home directory (/root, /home/user)
  • $USER — current username
  • $PATH — directories to search for commands
  • $SHELL — current shell path
  • $PWD — current working directory
  • $LANG — system locale
  • $EDITOR — default text editor (used by git, crontab, etc.)

Debugging environment issues

If a service isn't seeing an environment variable you set, check:

  1. Did you export it? (Required for child processes)
  2. Is the service running under systemd? (Needs Environment= in unit file)
  3. Did you restart the service after changing the config?
  4. Is it in the right file? (~/.bashrc vs ~/.profile)
  5. Did you source the file or start a new shell?

Quick debugging trick — inspect a running process's environment:

cat /proc/$(pgrep -f myapp)/environ | tr '\0' '\n'

This shows exactly which environment variables the running process actually sees, regardless of what you configured.

SKIP THE COMMANDS

Or just ask Claude on your server.

BareMetalServer.ai includes a built-in web terminal with Claude Code. Skip the documentation lookups — tell Claude what you need and it runs the commands directly on your server.

"Check Ubuntu version and update all packages"
"Install Docker and pull the latest nginx image"
"Set up a cron job to backup my database every 6 hours"

Claude runs commands, explains what it did, and fixes errors automatically. No SSH client, no memorizing flags, no documentation lookups.

Deploy a bare metal server in 5 minutes.

Full root access, NVMe storage, up to 100TB bandwidth. From $449/mo. Cancel anytime.