How to Set Up a Bash Profile, .bashrc and Environment Variables on RHEL 7

A well-configured shell environment can dramatically improve your productivity as a Linux administrator. On Red Hat Enterprise Linux 7, Bash is the default login shell and it reads several configuration files at startup, each serving a distinct purpose. Understanding the difference between .bash_profile and .bashrc, how environment variables are set and exported, and how to make customizations persistent and system-wide is essential knowledge for managing RHEL 7 systems effectively. This tutorial covers all of these topics with practical examples you can apply immediately.

Prerequisites

  • A RHEL 7 system with a standard user account and root or sudo access
  • Basic familiarity with the Bash command line
  • A text editor (vi, nano, or your preference)

Step 1: Understanding the Bash Startup File Hierarchy

Bash reads different configuration files depending on how the shell session is started. The two fundamental modes are login shells and non-login interactive shells.

  • Login shell — invoked when you log in via SSH, the console, or run bash --login. Reads /etc/profile first, then the first of ~/.bash_profile, ~/.bash_login, or ~/.profile that exists.
  • Non-login interactive shell — invoked when you open a terminal window in a GUI session or run bash directly. Reads only ~/.bashrc.

The practical consequence is:

  • Put environment variable exports and PATH changes in ~/.bash_profile (or have it source ~/.bashrc).
  • Put aliases, functions, and prompt customizations in ~/.bashrc.

The default ~/.bash_profile on RHEL 7 already sources ~/.bashrc with:

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

This means anything in ~/.bashrc is also available in login shells, which is the recommended setup.

Step 2: Editing .bash_profile for Login Shell Configuration

Open your profile file:

vi ~/.bash_profile

A typical RHEL 7 ~/.bash_profile looks like:

# .bash_profile

# Get the aliases and functions
if [ -f ~/.bashrc ]; then
    . ~/.bashrc
fi

# User specific environment and startup programs
PATH=$PATH:$HOME/.local/bin:$HOME/bin
export PATH

You can add additional login-only setup here, such as:

# Load NVM (Node Version Manager) for this user
export NVM_DIR="$HOME/.nvm"
[ -s "$NVM_DIR/nvm.sh" ] && . "$NVM_DIR/nvm.sh"

# Set Java home
export JAVA_HOME=/usr/lib/jvm/java-1.8.0-openjdk
export PATH=$JAVA_HOME/bin:$PATH

# Custom application directory
export MYAPP_HOME=/opt/myapp
export PATH=$MYAPP_HOME/bin:$PATH

Step 3: Editing .bashrc for Interactive Shell Configuration

~/.bashrc is the right place for aliases, shell functions, and settings that affect interactive use:

vi ~/.bashrc

Add aliases and useful shell options:

# Source global definitions
if [ -f /etc/bashrc ]; then
    . /etc/bashrc
fi

# Useful aliases
alias ll='ls -lh --color=auto'
alias la='ls -lah --color=auto'
alias ..='cd ..'
alias ...='cd ../..'
alias grep='grep --color=auto'
alias df='df -h'
alias du='du -h'
alias free='free -h'
alias vi='vim'

# Safety nets
alias rm='rm -i'
alias cp='cp -i'
alias mv='mv -i'

# System administration shortcuts
alias sctl='sudo systemctl'
alias jctl='sudo journalctl'
alias taillog='sudo tail -f /var/log/messages'

# Shell behavior options
shopt -s histappend         # append to history file, don't overwrite
shopt -s checkwinsize       # update LINES and COLUMNS after each command
shopt -s cdspell            # autocorrect minor cd typos

# History settings
HISTSIZE=10000
HISTFILESIZE=20000
HISTCONTROL=ignoredups:ignorespace
export HISTTIMEFORMAT="%F %T  "

Step 4: Working with Environment Variables and export

Environment variables are key-value pairs available to processes. In Bash, you create a shell variable with simple assignment, and make it an environment variable (visible to child processes) by using export:

# Shell variable — visible only in this shell
MYVAR="hello"

# Environment variable — inherited by child processes
export MYVAR="hello"

# One-liner: define and export in one step
export DATABASE_URL="postgresql://localhost/mydb"

# View all current environment variables
printenv

# View a specific variable
echo $DATABASE_URL
printenv DATABASE_URL

To make a variable available only for a single command:

DEBUG=true ./myscript.sh

Step 5: Customizing the PATH Variable

The PATH variable tells Bash where to look for executables. Modifying it correctly is one of the most common shell configuration tasks.

# Prepend a directory (takes priority over existing entries)
export PATH=/opt/custom/bin:$PATH

# Append a directory (lower priority)
export PATH=$PATH:/opt/custom/bin

# Add multiple directories at once
export PATH=$PATH:/opt/tool1/bin:/opt/tool2/bin

# Add user-local scripts directory
export PATH=$PATH:$HOME/.local/bin

# Verify the current PATH
echo $PATH | tr ':' 'n'

Always use $PATH when extending it — never replace it entirely, or you will lose access to standard system utilities. Place PATH modifications in ~/.bash_profile for user-specific changes, or in a file under /etc/profile.d/ for system-wide changes.

Step 6: System-Wide Environment Variables with /etc/profile.d/

For settings that should apply to all users, use the /etc/profile.d/ directory. Every .sh file in this directory is sourced by /etc/profile at login shell startup.

sudo vi /etc/profile.d/custom_env.sh

Example contents:

#!/bin/bash
# System-wide custom environment settings

export COMPANY_APP_HOME=/opt/companyapp
export PATH=$PATH:$COMPANY_APP_HOME/bin
export LOG_LEVEL=info
export TZ="America/New_York"

Make it executable:

sudo chmod +x /etc/profile.d/custom_env.sh

Changes take effect on next login. You can also source it immediately:

source /etc/profile.d/custom_env.sh

The /etc/environment File

RHEL 7 also supports /etc/environment for simple key=value pairs (no shell syntax). This file is read by PAM and applies to all sessions including non-login ones started by systemd services:

sudo vi /etc/environment
JAVA_HOME=/usr/lib/jvm/java-1.8.0-openjdk
EDITOR=vim
LANG=en_US.UTF-8

Note: /etc/environment does not support variable expansion like PATH=$PATH:/extra — only static values.

Step 7: Customizing the PS1 Prompt

The PS1 variable controls the appearance of your command prompt. A well-designed prompt can show your username, hostname, current directory, and even the exit status of the last command, all at a glance.

# Basic prompt: user@host:dir$
export PS1='u@h:w$ '

# Add color — green user@host, blue path
export PS1='[e[32m]u@h[e[0m]:[e[34m]w[e[0m]$ '

# Show exit code of last command in red if non-zero
export PS1='[e[32m]u@h[e[0m]:[e[34m]w[e[0m]$([ $? -ne 0 ] && echo "[e[31m][$?][e[0m]")$ '

Common PS1 escape sequences:

  • u — current username
  • h — short hostname
  • H — full hostname
  • w — current working directory (full path)
  • W — basename of current directory only
  • t — current time (HH:MM:SS)
  • $# if root, $ otherwise

Add your preferred PS1 line to ~/.bashrc to make it persistent.

Step 8: Using the source Command

After editing any Bash configuration file, changes do not take effect in the current shell session until you reload it. Use the source command (or its alias .) to apply changes without opening a new session:

# Reload .bashrc
source ~/.bashrc

# Equivalent shorthand
. ~/.bashrc

# Reload .bash_profile
source ~/.bash_profile

# Reload a profile.d file
source /etc/profile.d/custom_env.sh

It is good practice to test your .bashrc changes with source before logging out, so you can catch and fix any syntax errors without risking a broken login environment.

Conclusion

A clean and organized shell environment is the foundation of productive system administration. On RHEL 7, the key is understanding that ~/.bash_profile runs for login shells and ~/.bashrc runs for interactive non-login shells, with the standard RHEL setup already bridging the two by sourcing ~/.bashrc from ~/.bash_profile. Keep your environment variable exports and PATH manipulation in ~/.bash_profile, your aliases and prompt in ~/.bashrc, and use /etc/profile.d/ for settings that should apply to all system users. With these principles in place, your shell configuration will be maintainable, predictable, and easy to replicate across multiple systems.