Learn how to properly configure Linux environment variables and manage your PATH for development workflows. This guide covers temporary and persistent variables, user vs system-wide configuration, and troubleshooting common issues across Ubuntu, Debian, AlmaLinux, Rocky Linux, and Fedora.
Prerequisites
- Basic Linux command line knowledge
- Text editor access (nano, vim, or other)
- User account with sudo privileges
What this solves
Environment variables control how your Linux system and applications behave, storing configuration data like file paths, system settings, and application preferences. Proper environment variable management ensures your development tools, custom scripts, and applications can find required binaries and configuration files. This tutorial shows you how to set temporary and persistent environment variables, manage your PATH for custom binaries, and configure variables at user and system levels.
Understanding environment variables and scope
Environment variables are key-value pairs that define the operating environment for processes. They exist in different scopes: process-specific (temporary), user-specific (persistent for one user), and system-wide (persistent for all users).
View current environment variables
Start by examining your current environment to understand what variables are already set.
# View all environment variables
env
View specific variable
echo $PATH
echo $HOME
echo $USER
Search for variables containing specific text
env | grep -i java
Understanding variable scope levels
Different configuration files control variable scope. Here's the hierarchy from most specific to most general.
# User-specific files (loaded in order)
ls -la ~/.bashrc ~/.bash_profile ~/.profile
System-wide files
ls -la /etc/environment /etc/profile /etc/bash.bashrc
Setting temporary environment variables
Set variables for current session
Temporary variables only exist for the current shell session and disappear when you log out or close the terminal.
# Set a simple variable
export MY_APP_HOME="/opt/myapp"
export DATABASE_URL="postgresql://localhost:5432/mydb"
Verify the variable is set
echo $MY_APP_HOME
env | grep MY_APP
Set variables for single command
You can set environment variables that only apply to a single command execution.
# Run command with specific environment
DEBUG=true LOG_LEVEL=info myapp --start
Multiple variables for one command
JAVA_HOME=/usr/lib/jvm/java-11-openjdk PATH="$JAVA_HOME/bin:$PATH" java -version
Setting persistent environment variables
Configure user-specific variables
Add variables to your user's shell configuration files. The ~/.bashrc file is loaded for interactive non-login shells.
# Add these lines to the end of ~/.bashrc
export EDITOR="vim"
export BROWSER="firefox"
export MY_PROJECT_HOME="$HOME/projects"
export DATABASE_URL="postgresql://localhost:5432/devdb"
Make custom bin directory available
export PATH="$HOME/bin:$HOME/.local/bin:$PATH"
Apply user configuration changes
Reload your shell configuration to apply the new variables without logging out.
# Reload bash configuration
source ~/.bashrc
Or start a new shell
bash
Verify variables are set
echo $EDITOR
echo $MY_PROJECT_HOME
Configure system-wide variables
Set variables that apply to all users by editing system configuration files. Use /etc/environment for simple variable definitions.
# Simple variable definitions (no export keyword needed)
JAVA_HOME="/usr/lib/jvm/default-java"
GOROOT="/usr/local/go"
GOPATH="/opt/go"
Append to existing PATH
PATH="/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/local/go/bin"
Create custom environment file
For complex configurations, create a custom file in /etc/profile.d/ that gets loaded automatically.
sudo nano /etc/profile.d/development.sh
#!/bin/bash
Development environment variables
Node.js configuration
export NODE_ENV="development"
export NPM_CONFIG_PREFIX="$HOME/.npm-global"
Python configuration
export PYTHONPATH="/opt/python/lib:$PYTHONPATH"
export PIP_REQUIRE_VIRTUALENV="true"
Custom development tools
export DEV_TOOLS_PATH="/opt/dev-tools/bin"
export PATH="$DEV_TOOLS_PATH:$NPM_CONFIG_PREFIX/bin:$PATH"
Set proper permissions for custom profile
Ensure the custom profile file has correct permissions to be executed by the system.
# Set correct permissions
sudo chmod 644 /etc/profile.d/development.sh
Verify file permissions
ls -la /etc/profile.d/development.sh
Managing PATH variable for custom binaries
Understanding PATH structure
The PATH variable tells the system where to look for executable files. It's a colon-separated list of directories searched in order.
# View current PATH
echo $PATH
View PATH with each directory on separate line
echo $PATH | tr ':' '\n'
Check which executable will be used
which python3
which node
type -a python3
Add custom directories to PATH
Prepend your custom directories to PATH so they're searched before system directories. This allows you to override system binaries safely.
# Add custom binary directories to PATH
Order matters: first directories are searched first
export PATH="$HOME/bin:$HOME/.local/bin:$PATH"
Add development tools
export PATH="/opt/maven/bin:/opt/gradle/bin:$PATH"
Add language-specific paths
export PATH="$HOME/.cargo/bin:$HOME/go/bin:$PATH"
Create and test custom binary directory
Set up a personal bin directory for your custom scripts and verify PATH configuration works correctly.
# Create personal bin directory
mkdir -p $HOME/bin
Create a test script
cat > $HOME/bin/mytest << 'EOF'
#!/bin/bash
echo "Custom script in PATH working!"
echo "Script location: $0"
EOF
Make script executable
chmod 755 $HOME/bin/mytest
Reload PATH and test custom binary
Apply the PATH changes and verify your custom binary is accessible from any directory.
# Reload shell configuration
source ~/.bashrc
Test custom binary is found
which mytest
mytest
Change to different directory and test
cd /tmp
mytest
User vs system-wide environment configuration
Choose appropriate configuration level
Understand when to use user-specific vs system-wide configuration based on your needs.
# Check current user configuration files
ls -la ~/.bashrc ~/.bash_profile ~/.profile ~/.bash_login
Check system-wide configuration files
ls -la /etc/environment /etc/profile /etc/bash.bashrc /etc/profile.d/
| Scope | Configuration File | Use Case | Loaded When |
|---|---|---|---|
| User Interactive | ~/.bashrc | Personal development environment | Non-login interactive shells |
| User Login | ~/.bash_profile or ~/.profile | Personal login environment | Login shells |
| System Simple | /etc/environment | Basic system-wide variables | All sessions |
| System Advanced | /etc/profile.d/*.sh | Complex system configurations | Login shells |
Test configuration loading order
Create test variables in different files to understand the loading order and precedence.
# Add test variable to user config
echo 'export CONFIG_TEST="user-bashrc"' >> ~/.bashrc
Add to system config
echo 'CONFIG_TEST="system-environment"' | sudo tee -a /etc/environment
Start new shell and check which value wins
bash
echo $CONFIG_TEST
Development workflow environment setup
Configure development environment variables
Set up a comprehensive development environment with commonly needed variables for multiple programming languages and tools.
# Editor and terminal preferences
export EDITOR="vim"
export PAGER="less"
export BROWSER="firefox"
Development directories
export PROJECTS_HOME="$HOME/projects"
export WORKSPACE="$HOME/workspace"
Language environments
export JAVA_HOME="/usr/lib/jvm/default-java"
export GOPATH="$HOME/go"
export GOROOT="/usr/local/go"
export CARGO_HOME="$HOME/.cargo"
export RUSTUP_HOME="$HOME/.rustup"
Node.js and npm
export NODE_ENV="development"
export NPM_CONFIG_PREFIX="$HOME/.npm-global"
Python development
export PYTHONDONTWRITEBYTECODE="1"
export PYTHONUNBUFFERED="1"
export PIPENV_VENV_IN_PROJECT="1"
Database connections
export DATABASE_URL="postgresql://localhost:5432/devdb"
export REDIS_URL="redis://localhost:6379/0"
API keys and secrets (use for development only)
export API_KEY="dev_api_key_here"
export SECRET_KEY="dev_secret_key_here"
Configure comprehensive PATH for development
Build a PATH that includes all development tools and custom binaries in the correct priority order.
# Build PATH with proper precedence
Personal bins first (highest priority)
export PATH="$HOME/bin:$HOME/.local/bin:$PATH"
Language-specific binaries
export PATH="$GOROOT/bin:$GOPATH/bin:$PATH"
export PATH="$CARGO_HOME/bin:$PATH"
export PATH="$NPM_CONFIG_PREFIX/bin:$PATH"
export PATH="$HOME/.python/bin:$PATH"
Development tools
export PATH="/opt/maven/bin:/opt/gradle/bin:$PATH"
export PATH="$JAVA_HOME/bin:$PATH"
Docker and container tools
export PATH="$HOME/.docker/bin:$PATH"
Custom development utilities
export PATH="/opt/dev-tools/bin:$PATH"
Create project-specific environment loader
Create a system to automatically load project-specific environment variables when entering project directories.
# Create project environment loader function
cat >> ~/.bashrc << 'EOF'
Auto-load project environment variables
load_project_env() {
local dir="$PWD"
while [[ "$dir" != "/" ]]; do
if [[ -f "$dir/.env" ]]; then
echo "Loading environment from $dir/.env"
set -a
source "$dir/.env"
set +a
break
fi
dir=$(dirname "$dir")
done
}
Auto-load when changing directories
cd() {
builtin cd "$@" && load_project_env
}
EOF
Create sample project environment file
Demonstrate project-specific environment configuration with a sample .env file.
# Create sample project directory
mkdir -p $HOME/projects/sample-app
cd $HOME/projects/sample-app
# Project-specific environment variables
APP_NAME="sample-app"
APP_ENV="development"
APP_DEBUG="true"
APP_PORT="3000"
Database configuration
DATABASE_URL="postgresql://localhost:5432/sample_app_dev"
REDIS_URL="redis://localhost:6379/1"
API endpoints
API_BASE_URL="http://localhost:8080/api/v1"
EXTERNAL_API_KEY="dev_key_12345"
Build configuration
BUILD_TARGET="debug"
OUTPUT_DIR="./dist"
SOURCE_DIR="./src"
Verify your setup
# Test environment variable loading
source ~/.bashrc
Verify key variables are set
echo "Editor: $EDITOR"
echo "Java Home: $JAVA_HOME"
echo "Go Path: $GOPATH"
echo "Projects Home: $PROJECTS_HOME"
Test PATH configuration
echo "PATH directories:"
echo $PATH | tr ':' '\n' | nl
Test custom binary accessibility
which mytest
type -a python3 java node
Test project environment loading
cd $HOME/projects/sample-app
echo "App Name: $APP_NAME"
echo "App Port: $APP_PORT"
Verify system-wide variables
sudo -u www-data printenv | grep -E '(JAVA_HOME|GOROOT)'
Check variable persistence after new login
ssh localhost 'echo $EDITOR; echo $PATH' 2>/dev/null || echo "Test SSH login to verify persistence"
Troubleshooting common environment variable issues
| Symptom | Cause | Fix |
|---|---|---|
| Variables not available in GUI applications | GUI doesn't load shell config | Add variables to ~/.profile or /etc/environment |
| PATH changes not persistent | Added to wrong config file | Add to ~/.bashrc or ~/.profile, reload with source |
| Command not found after adding to PATH | Directory doesn't exist or no execute permission | Check directory exists: ls -la /path/to/bin |
| Variables empty in cron jobs | Cron doesn't load user environment | Set variables in crontab or source ~/.bashrc in script |
| sudo commands can't find binaries | sudo resets PATH by default | Use sudo -E or add to /etc/environment |
| Variables not available in SSH sessions | SSH doesn't load .bashrc by default | Add variables to ~/.profile or force .bashrc load |
| Conflicting variable values | Multiple files setting same variable | Check precedence: user configs override system configs |
| Special characters break variables | Improper quoting or escaping | Use double quotes and escape special characters |
Security considerations
Secure sensitive environment variables
Protect sensitive data like API keys and passwords by setting appropriate file permissions and using secure storage methods.
# Create secure environment file for sensitive variables
touch ~/.env.secrets
chmod 600 ~/.env.secrets
Add sensitive variables
cat > ~/.env.secrets << 'EOF'
Sensitive environment variables - keep secure
export DATABASE_PASSWORD="secure_password_here"
export API_SECRET_KEY="secret_key_here"
export JWT_SECRET="jwt_secret_here"
EOF
Load secrets in .bashrc conditionally
echo '[ -f ~/.env.secrets ] && source ~/.env.secrets' >> ~/.bashrc
Audit environment variable security
Regularly review your environment configuration for security issues and exposed sensitive data.
# Check file permissions on environment files
ls -la ~/.bashrc ~/.bash_profile ~/.profile ~/.env*
ls -la /etc/environment /etc/profile /etc/profile.d/*
Look for potentially sensitive variables in environment
env | grep -i -E '(password|secret|key|token)'
Check if sensitive files are readable by others
find ~ -name '.env' -type f -not -perm 600 2>/dev/null
Next steps
- Configure Linux system resource limits with systemd and ulimit for application performance
- Configure Linux user session limits with systemd and pam_limits for resource management
- Set up Linux development environment with Docker and Docker Compose
- Configure shell aliases and functions for development productivity
- Set up development database environments with environment variables
Automated install script
Run this to automate the entire setup
#!/usr/bin/env bash
set -euo pipefail
# Colors for output
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
NC='\033[0m' # No Color
# Global variables
SCRIPT_NAME="$(basename "$0")"
BACKUP_DIR="/tmp/env_backup_$(date +%s)"
# Usage function
usage() {
echo "Usage: $SCRIPT_NAME [OPTIONS]"
echo "Configure Linux environment variables and PATH management"
echo ""
echo "Options:"
echo " --user-only Configure only user-level environment"
echo " --system-only Configure only system-level environment"
echo " --custom-vars FILE Load custom variables from file"
echo " --help Show this help message"
echo ""
echo "Example:"
echo " $SCRIPT_NAME --user-only"
echo " $SCRIPT_NAME --custom-vars /path/to/vars.conf"
exit 1
}
# Logging functions
log_info() { echo -e "${GREEN}[INFO]${NC} $1"; }
log_warn() { echo -e "${YELLOW}[WARN]${NC} $1"; }
log_error() { echo -e "${RED}[ERROR]${NC} $1"; }
# Cleanup function for rollback
cleanup() {
if [[ -d "$BACKUP_DIR" ]]; then
log_warn "Script failed. Restoring backups from $BACKUP_DIR"
if [[ -f "$BACKUP_DIR/bashrc" ]]; then
cp "$BACKUP_DIR/bashrc" "$HOME/.bashrc" 2>/dev/null || true
fi
if [[ -f "$BACKUP_DIR/profile" ]]; then
cp "$BACKUP_DIR/profile" "$HOME/.profile" 2>/dev/null || true
fi
if [[ -f "$BACKUP_DIR/environment" ]] && [[ "$EUID" -eq 0 ]]; then
cp "$BACKUP_DIR/environment" /etc/environment 2>/dev/null || true
fi
fi
}
# Set up error trap
trap cleanup ERR
# Detect distribution
detect_distro() {
if [[ -f /etc/os-release ]]; then
. /etc/os-release
case "$ID" in
ubuntu|debian)
PKG_MGR="apt"
PKG_INSTALL="apt install -y"
PROFILE_DIR="/etc/profile.d"
;;
almalinux|rocky|centos|rhel|ol|fedora)
PKG_MGR="dnf"
PKG_INSTALL="dnf install -y"
PROFILE_DIR="/etc/profile.d"
# Use yum for older versions
if ! command -v dnf &> /dev/null; then
PKG_MGR="yum"
PKG_INSTALL="yum install -y"
fi
;;
amzn)
PKG_MGR="yum"
PKG_INSTALL="yum install -y"
PROFILE_DIR="/etc/profile.d"
;;
*)
log_error "Unsupported distribution: $ID"
exit 1
;;
esac
export PKG_MGR PKG_INSTALL PROFILE_DIR
log_info "Detected distribution: $PRETTY_NAME"
else
log_error "Cannot detect distribution - /etc/os-release not found"
exit 1
fi
}
# Create backup directory
create_backup() {
mkdir -p "$BACKUP_DIR"
log_info "Created backup directory: $BACKUP_DIR"
# Backup user files
[[ -f "$HOME/.bashrc" ]] && cp "$HOME/.bashrc" "$BACKUP_DIR/bashrc"
[[ -f "$HOME/.profile" ]] && cp "$HOME/.profile" "$BACKUP_DIR/profile"
# Backup system files if root
if [[ "$EUID" -eq 0 ]]; then
[[ -f /etc/environment ]] && cp /etc/environment "$BACKUP_DIR/environment"
fi
}
# Configure user-level environment variables
configure_user_environment() {
log_info "Configuring user-level environment variables"
# Ensure .bashrc exists
if [[ ! -f "$HOME/.bashrc" ]]; then
touch "$HOME/.bashrc"
chmod 644 "$HOME/.bashrc"
fi
# Add development environment variables to .bashrc
cat >> "$HOME/.bashrc" << 'EOF'
# Development Environment Variables - Added by environment setup script
export EDITOR="${EDITOR:-vim}"
export BROWSER="${BROWSER:-firefox}"
export DEVELOPMENT_MODE="true"
# Custom paths
export MY_SCRIPTS_HOME="$HOME/scripts"
export MY_PROJECTS_HOME="$HOME/projects"
# Add user bin directories to PATH
export PATH="$HOME/bin:$HOME/.local/bin:$PATH"
# Node.js configuration
export NPM_CONFIG_PREFIX="$HOME/.npm-global"
export PATH="$NPM_CONFIG_PREFIX/bin:$PATH"
# Python configuration
export PIP_REQUIRE_VIRTUALENV="false"
export PYTHONPATH="$HOME/.local/lib/python:$PYTHONPATH"
# Go configuration
export GOPATH="$HOME/go"
export PATH="$GOPATH/bin:$PATH"
EOF
# Create user bin directories
mkdir -p "$HOME/bin" "$HOME/.local/bin" "$HOME/scripts" "$HOME/projects"
chmod 755 "$HOME/bin" "$HOME/.local/bin" "$HOME/scripts" "$HOME/projects"
log_info "User environment configuration completed"
}
# Configure system-level environment variables
configure_system_environment() {
if [[ "$EUID" -ne 0 ]]; then
log_warn "Skipping system configuration - root privileges required"
return 0
fi
log_info "Configuring system-level environment variables"
# Configure /etc/environment
if [[ ! -f /etc/environment ]]; then
touch /etc/environment
chmod 644 /etc/environment
fi
# Add system-wide variables to /etc/environment
cat >> /etc/environment << 'EOF'
# System Development Environment - Added by environment setup script
JAVA_HOME="/usr/lib/jvm/default-java"
SYSTEM_DEV_MODE="true"
EOF
# Create custom profile script
cat > "$PROFILE_DIR/99-development.sh" << 'EOF'
#!/bin/bash
# Development environment configuration
# Added by environment setup script
# System development paths
export DEV_TOOLS_PATH="/opt/dev-tools"
export SYSTEM_SCRIPTS_PATH="/opt/scripts"
# Add system development paths to PATH
if [[ ":$PATH:" != *":$DEV_TOOLS_PATH/bin:"* ]]; then
export PATH="$DEV_TOOLS_PATH/bin:$PATH"
fi
if [[ ":$PATH:" != *":$SYSTEM_SCRIPTS_PATH:"* ]]; then
export PATH="$SYSTEM_SCRIPTS_PATH:$PATH"
fi
# Database configuration for development
export DB_HOST="${DB_HOST:-localhost}"
export DB_PORT="${DB_PORT:-5432}"
# Development tool configurations
export DOCKER_BUILDKIT="1"
export COMPOSE_DOCKER_CLI_BUILD="1"
EOF
chmod 755 "$PROFILE_DIR/99-development.sh"
chown root:root "$PROFILE_DIR/99-development.sh"
# Create system directories
mkdir -p /opt/dev-tools/bin /opt/scripts
chmod 755 /opt/dev-tools /opt/dev-tools/bin /opt/scripts
chown root:root /opt/dev-tools /opt/dev-tools/bin /opt/scripts
log_info "System environment configuration completed"
}
# Load custom variables from file
load_custom_variables() {
local custom_file="$1"
if [[ ! -f "$custom_file" ]]; then
log_error "Custom variables file not found: $custom_file"
exit 1
fi
log_info "Loading custom variables from: $custom_file"
# Validate and source custom variables
if bash -n "$custom_file"; then
while IFS='=' read -r key value; do
# Skip empty lines and comments
[[ -z "$key" || "$key" =~ ^[[:space:]]*# ]] && continue
# Export the variable
export "$key=$value"
echo "export $key=\"$value\"" >> "$HOME/.bashrc"
done < <(grep -E '^[A-Z_][A-Z0-9_]*=' "$custom_file")
log_info "Custom variables loaded successfully"
else
log_error "Custom variables file contains syntax errors"
exit 1
fi
}
# Verify environment configuration
verify_configuration() {
log_info "Verifying environment configuration"
# Test loading .bashrc
if bash -n "$HOME/.bashrc"; then
log_info "✓ .bashrc syntax is valid"
else
log_error "✗ .bashrc contains syntax errors"
return 1
fi
# Test environment variable access
if source "$HOME/.bashrc" && [[ -n "${DEVELOPMENT_MODE:-}" ]]; then
log_info "✓ User environment variables loaded correctly"
else
log_warn "⚠ User environment variables may not be loading correctly"
fi
# Check directory creation
local dirs=("$HOME/bin" "$HOME/.local/bin" "$HOME/scripts" "$HOME/projects")
for dir in "${dirs[@]}"; do
if [[ -d "$dir" ]]; then
log_info "✓ Directory exists: $dir"
else
log_error "✗ Directory missing: $dir"
fi
done
# Check system configuration if root
if [[ "$EUID" -eq 0 ]] && [[ -f "$PROFILE_DIR/99-development.sh" ]]; then
if bash -n "$PROFILE_DIR/99-development.sh"; then
log_info "✓ System profile script is valid"
else
log_error "✗ System profile script contains errors"
fi
fi
log_info "Configuration verification completed"
}
# Main function
main() {
local user_only=false
local system_only=false
local custom_vars_file=""
# Parse arguments
while [[ $# -gt 0 ]]; do
case $1 in
--user-only)
user_only=true
shift
;;
--system-only)
system_only=true
shift
;;
--custom-vars)
custom_vars_file="$2"
shift 2
;;
--help)
usage
;;
*)
log_error "Unknown option: $1"
usage
;;
esac
done
echo "[1/6] Detecting Linux distribution"
detect_distro
echo "[2/6] Creating backup of existing configuration"
create_backup
if [[ "$system_only" != true ]]; then
echo "[3/6] Configuring user-level environment variables"
configure_user_environment
else
echo "[3/6] Skipping user configuration (--system-only specified)"
fi
if [[ "$user_only" != true ]]; then
echo "[4/6] Configuring system-level environment variables"
configure_system_environment
else
echo "[4/6] Skipping system configuration (--user-only specified)"
fi
if [[ -n "$custom_vars_file" ]]; then
echo "[5/6] Loading custom variables"
load_custom_variables "$custom_vars_file"
else
echo "[5/6] No custom variables file specified"
fi
echo "[6/6] Verifying configuration"
verify_configuration
log_info "Environment variable configuration completed successfully!"
log_info "Restart your terminal or run 'source ~/.bashrc' to apply changes"
log_info "Backup files are available in: $BACKUP_DIR"
}
# Run main function with all arguments
main "$@"
Review the script before running. Execute with: bash install.sh