Back to blog
How to Set Environment Variables in Linux (export, .bashrc, systemd)

How to Set Environment Variables in Linux (export, .bashrc, systemd)

Step-by-step guide to setting environment variables in Linux. Covers export for session variables, .bashrc and .zshrc for persistence, /etc/environment for system-wide config, and systemd EnvironmentFile for services.

February 16, 2026by Patrick Gerrits
environment-variableslinuxdevopsshell

How to Set Environment Variables in Linux

You SSH into a production server at 2 AM because the deployment failed. The application won't start. After digging through logs, you realize the problem: someone forgot to set DATABASE_URL on this instance. You need to set an environment variable in Linux, and you need it working now—not just for this session, but permanently.

Environment variables are fundamental to how Linux systems work. They control everything from where your shell looks for commands (PATH) to how applications connect to databases. Understanding how to set up environment variables in Linux properly means understanding the difference between temporary session variables, user-specific persistent variables, and system-wide configuration.

This guide covers the practical mechanics of setting environment variables across different scopes, shell configurations, and systemd services. You'll learn which approach to use depending on whether you're configuring your personal development environment or deploying applications to production servers.

Setting Variables with export

The export command sets environment variables in your current shell session. It's the most immediate way to define a variable, but it only lasts until you close your terminal or log out.

Basic syntax:

export VARIABLE_NAME="value"

Real example:

export DATABASE_URL="postgresql://user:pass@localhost:5432/mydb"
export API_KEY="sk_live_abc123xyz"
export NODE_ENV="production"

You can set multiple variables in sequence or chain them on one line:

export PORT=3000 HOSTNAME=api.example.com LOG_LEVEL=debug

To verify a variable is set, use echo:

echo $DATABASE_URL

Variables set with export are available to the current shell and any child processes it spawns. If you run a script or start an application from that terminal, it inherits those variables. Open a new terminal tab, though, and they're gone.

This approach is perfect for:

  • Quick testing and debugging
  • One-off commands that need specific configuration
  • Temporary overrides of existing variables

But if you need the variable to persist across sessions, you need to add it to a shell configuration file.

Shell Configuration Files: Where to Put Persistent Variables

Linux shells read configuration files when they start. The file they read depends on whether the shell is a login shell, an interactive shell, or a non-interactive shell. This is where things get confusing.

Login vs Interactive vs Non-Interactive Shells

A login shell runs when you log in via SSH or a TTY console. It reads /etc/profile, then ~/.bash_profile, ~/.bash_login, or ~/.profile (in that order, stopping at the first one it finds).

An interactive non-login shell runs when you open a new terminal window in your desktop environment. It reads ~/.bashrc.

A non-interactive shell runs when you execute a script. It doesn't read the standard config files unless the script explicitly sources them.

The Files Explained

~/.bashrc: Sourced by interactive non-login shells. This is where most people put their aliases, functions, and environment variables for daily use. If you're using a graphical terminal emulator, this is what gets loaded every time you open a new terminal window.

~/.bash_profile: Sourced by login shells. It typically sources ~/.bashrc to ensure consistency between login and non-login shells. Many people put export PATH modifications here, but honestly, just putting them in ~/.bashrc and sourcing that from ~/.bash_profile is cleaner.

~/.profile: A POSIX-compliant fallback used by shells other than Bash (like sh or dash). If you're writing shell-agnostic configuration, use this. But if you're running Bash, ~/.bashrc and ~/.bash_profile are more specific.

~/.zshrc: If you're using Zsh (the default on macOS since Catalina, and increasingly popular on Linux), this is your main configuration file. Zsh doesn't distinguish between login and non-login shells the same way Bash does—it just uses ~/.zshrc for interactive shells.

Opinionated Take: Just Use .bashrc

If you're unsure where to put variables, put them in ~/.bashrc and add this to the top of your ~/.bash_profile:

if [ -f ~/.bashrc ]; then
    . ~/.bashrc
fi

This ensures variables are available in both login and interactive shells. You maintain one file, and everything works.

Setting Variables in Shell Config Files

Open ~/.bashrc (or ~/.zshrc if you use Zsh):

nano ~/.bashrc

Add your variables at the bottom:

export EDITOR=vim
export VISUAL=vim
export BROWSER=firefox
export GOPATH="$HOME/go"
export PATH="$GOPATH/bin:$PATH"
export JAVA_HOME=/usr/lib/jvm/java-17-openjdk-amd64

Save the file and reload it:

source ~/.bashrc

Now those variables persist across all future terminal sessions.

System-Wide Environment Variables

User-specific config files like ~/.bashrc only affect your user account. If you're setting variables that should apply to all users on the system (like JAVA_HOME or custom PATH additions for system-wide tools), you need system-wide configuration.

/etc/environment

This is the simplest system-wide configuration file. It's read by PAM (Pluggable Authentication Modules) during login and applies to all users regardless of their shell.

Syntax is straightforward—no export needed:

PATH="/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin"
JAVA_HOME="/usr/lib/jvm/java-17-openjdk-amd64"
LANG="en_US.UTF-8"

Variables here are static. You can't use shell expansions like $HOME or $PATH because /etc/environment isn't processed by a shell.

/etc/profile

This file is sourced by login shells for all users. It's a shell script, so you can use export, variable expansions, and conditionals.

export EDITOR=nano
export JAVA_HOME=/usr/lib/jvm/default-java

# Add custom binary directory to PATH
if [ -d /opt/custom/bin ]; then
    export PATH="/opt/custom/bin:$PATH"
fi

Changes here require users to log out and back in (or source the file manually).

/etc/profile.d/

Instead of editing /etc/profile directly, it's cleaner to drop custom scripts into /etc/profile.d/. The system automatically sources all .sh files in this directory during login.

Create a custom config file:

sudo nano /etc/profile.d/custom-env.sh

Add your variables:

export APP_ENV=production
export LOG_LEVEL=info
export DATA_DIR=/srv/data

Make it executable:

sudo chmod +x /etc/profile.d/custom-env.sh

This approach keeps customizations modular. Package managers can drop their own environment scripts here without conflicting with system defaults.

Which System-Wide File to Use?

  • /etc/environment: For simple, static variables that don't need shell logic. Applies to all login mechanisms (SSH, GUI, TTY).
  • /etc/profile: For variables that need shell expansion or conditionals. Only applies to login shells.
  • /etc/profile.d/: Best practice for custom system-wide variables. Keeps configuration modular and maintainable.

Environment Variables in Systemd Services

When you run applications as systemd services, they don't inherit your shell's environment. You need to define variables directly in the service unit file.

Inline Environment Variables

Open or create a service file:

sudo nano /etc/systemd/system/myapp.service

Use the Environment= directive:

[Unit]
Description=My Application
After=network.target

[Service]
Type=simple
User=appuser
WorkingDirectory=/opt/myapp
ExecStart=/opt/myapp/bin/start

Environment="NODE_ENV=production"
Environment="PORT=3000"
Environment="DATABASE_URL=postgresql://localhost/mydb"

[Install]
WantedBy=multi-user.target

Each Environment= line sets one variable. Quotes are optional but recommended for values with special characters.

Environment Files

For services with many variables or sensitive configuration, use EnvironmentFile= to load variables from a file:

[Service]
EnvironmentFile=/etc/myapp/env
ExecStart=/opt/myapp/bin/start

Create the environment file:

sudo nano /etc/myapp/env

Use simple KEY=VALUE pairs (no export):

NODE_ENV=production
PORT=3000
DATABASE_URL=postgresql://user:pass@localhost/mydb
API_KEY=sk_live_abc123xyz
LOG_LEVEL=info

Protect sensitive files with proper permissions:

sudo chown root:root /etc/myapp/env
sudo chmod 600 /etc/myapp/env

After editing service files, reload systemd and restart the service:

sudo systemctl daemon-reload
sudo systemctl restart myapp

Using EnvironmentFile= keeps secrets out of service definitions and makes it easier to manage configuration across environments (dev, staging, production).

Viewing Environment Variables

Different commands show different scopes of variables. Knowing which to use matters when debugging.

printenv

Displays environment variables available to the current process:

printenv

Show a specific variable:

printenv PATH
printenv HOME

This is the most common command for checking environment variables. It only shows exported variables (not shell-local variables).

env

Similar to printenv, but can also set variables for a single command:

env NODE_ENV=development npm start

This runs npm start with NODE_ENV set to development for that command only—it doesn't persist.

Run env without arguments to list all environment variables:

env

set

Shows all variables—both environment variables and shell-local variables. The output is verbose because it includes shell functions and local variables.

set | grep DATABASE_URL

Use set when you're debugging and need to see everything. For most purposes, printenv is cleaner.

Persistent vs Session-Scoped Variables

Understanding scope is critical:

ScopeMethodDurationUse Case
Current session onlyexport VAR=valueUntil terminal closesTesting, debugging, one-off commands
User-specific, persistentAdd to ~/.bashrc or ~/.zshrcEvery new shell sessionPersonal development environment
User-specific, login onlyAdd to ~/.bash_profileLogin shells onlyVariables that only need to be set once per login
System-wide, all users/etc/environment or /etc/profile.d/All user sessionsSystem tools, language runtimes (JAVA_HOME, etc.)
Systemd serviceEnvironment= or EnvironmentFile=Service lifetimeApplication configuration, database URLs

For quick experimentation, use export. For personal development, use ~/.bashrc. For system-wide configuration, use /etc/profile.d/. For services, use systemd environment files.

Practical Examples

Modifying PATH

The PATH variable tells the shell where to look for executable files. Adding a custom directory:

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

This prepends ~/bin to the existing PATH. Order matters—directories at the front are searched first.

To make it persistent, add it to ~/.bashrc:

# Custom scripts directory
export PATH="$HOME/bin:$PATH"

For system-wide changes (like adding /opt/custom/bin for all users):

sudo nano /etc/profile.d/custom-path.sh
export PATH="/opt/custom/bin:$PATH"

Application Configuration

Set database connection strings, API keys, and application modes:

export DATABASE_URL="postgresql://user:pass@localhost:5432/mydb"
export REDIS_URL="redis://localhost:6379/0"
export API_KEY="sk_live_abc123xyz"
export APP_ENV="production"

For a Node.js app running as a systemd service:

sudo nano /etc/systemd/system/nodeapp.service
[Service]
EnvironmentFile=/etc/nodeapp/env
ExecStart=/usr/bin/node /opt/nodeapp/server.js
sudo nano /etc/nodeapp/env
NODE_ENV=production
DATABASE_URL=postgresql://user:pass@localhost:5432/nodedb
PORT=3000

Language Runtime Configuration

Java applications need JAVA_HOME:

export JAVA_HOME=/usr/lib/jvm/java-17-openjdk-amd64
export PATH="$JAVA_HOME/bin:$PATH"

Python virtual environments activate by modifying PATH:

source ~/projects/myapp/venv/bin/activate

This prepends the virtual environment's bin directory to PATH, so python and pip resolve to the virtual environment's copies.

Go projects use GOPATH:

export GOPATH="$HOME/go"
export PATH="$GOPATH/bin:$PATH"

Add these to ~/.bashrc if you work with these languages regularly.

Conditional Environment Variables

Sometimes you want variables set only under specific conditions. In ~/.bashrc:

# Use system-specific config
if [ -f ~/.env.local ]; then
    source ~/.env.local
fi

# Development shortcuts
if [ "$USER" = "dev" ]; then
    export DEBUG=true
    export LOG_LEVEL=debug
fi

# Production safety
if [ "$HOSTNAME" = "prod-server" ]; then
    export NODE_ENV=production
    export DEBUG=false
fi

This pattern keeps environment-specific configuration clean and maintainable.

Managing Variables Across Multiple Environments

Manually editing config files on every server gets tedious fast. Production, staging, and development environments each need different values for DATABASE_URL, API_KEY, and dozens of other variables. You end up SSHing into servers, editing /etc/myapp/env, restarting services, and hoping you didn't typo a connection string.

For teams managing environment variables across multiple projects and environments, EnvManager (free tier available) centralizes this workflow so you're not manually editing config files on every server. You define variables once, organize them by environment, and pull them into your deployment pipeline. It's the difference between maintaining scattered .env files across 20 servers and having a single source of truth.

But whether you use a dedicated tool or stick with config files, the underlying Linux mechanisms are the same. Understanding export, shell config files, system-wide settings, and systemd environment files gives you the foundation to troubleshoot when things break at 2 AM.

Try EnvManager free →

Ready to manage your environment variables securely?

EnvManager helps teams share secrets safely, sync configurations across platforms, and maintain audit trails.

Get started for free

Get DevOps tips in your inbox

Weekly security tips, environment management best practices, and product updates.

No spam. Unsubscribe anytime.