bashrc and bash_profile are easy to confuse because both live in your home directory
and both customize Bash. They answer different questions: was this shell started as a
login shell, and is it interactive? Those two flags decide whether your
~/.profile chain or ~/.bashrc runs—and on many laptops both run over the course of
a normal day, just not in the same order.
Tested all the commands and file snippets from this article on Ubuntu 25.04, kernel 6.14.0-37-generic, Bash 5.2.37.
For where to export long-lived environment variables more broadly, see where to set environment variables in Linux.
Bashrc vs bash_profile: what actually differs
Bash does not read “everything every time.” Roughly:
- Login shell (first text session, many SSH sessions,
su -,bash --login): after/etc/profile, Bash looks for the first existing file in this order:~/.bash_profile,~/.bash_login,~/.profile, and runs that file. It does not read all of them. - Interactive non-login shell (new terminal tab on a desktop where the graphical
session already started a login shell, or plain
bashfrom an existing session): reads~/.bashrc(unless--norc), and on many distributions something under/etc(for example/etc/bash.bashrc) may already be wired in from there. - Non-interactive shell (running
bash -c '…'or a script): does not read~/.bashrcby default. Optional setup goes throughBASH_ENV, not through “rc means every invocation.”
So bash_profile vs bashrc is not “pick a style file”; it is login profile chain vs interactive per-shell rc. The official rules live under Invocation in the GNU Bash manual.
.bashrc vs .bash_profile vs .profile
.bash_profile: Bash-specific login hook. Common on RHEL / Fedora / Rocky style/etc/skelsetups; often ends withsource ~/.bashrcso login shells still pick up aliases and prompt tweaks..profile: traditional login file shared withsh. On Debian / Ubuntu, new users typically get~/.profile(not~/.bash_profile) and that file sources~/.bashrcwhen the shell is Bash—so.profilevs.bashrcis “login wrapper” vs “per interactive Bash,” not either-or.
Ubuntu’s stock ~/.profile begins with the idea that .bashrc is for
non-login Bash shells, then explicitly includes it when $BASH_VERSION is set:
# if running bash
if [ -n "$BASH_VERSION" ]; then
# include .bashrc if it exists
if [ -f "$HOME/.bashrc" ]; then
. "$HOME/.bashrc"
fi
fiStock ~/.bashrc on the same systems usually returns immediately when the
shell is not interactive, so sourcing it from .profile does not slow down
non-interactive Bash invocations with a pile of alias definitions.
bashrc vs profile (and “bash profile”)
Searchers say bash profile when they mean “the file that runs when I log in.” On
Ubuntu that is commonly ~/.profile, not ~/.bash_profile. Remember the
first match wins rule: if you create ~/.bash_profile, Bash will use it for
login shells and will not automatically read ~/.profile unless you
source it yourself—easy foot-gun when copying snippets from the web.
What to edit: PATH vs day-to-day shell tweaks
Practical split most teams use:
- Put environment exports that must exist for login sessions—
PATH,LANG, tool*_HOMEvariables—in~/.profileor~/.bash_profile(whichever your distro actually uses for login), or in/etc/profile.d/*.shfor machine-wide policy. - Put interactive-only settings—
PS1, manyalias/bindentries,shopt, completion hooks—in~/.bashrc, guarded by the usualcase $- in *i*) … *) return;; esacblock so non-interactive shells exit early.
If something must exist in GUI apps launched from a desktop session, the answer
often moves up the stack (display-manager environment, ~/.config/environment.d
on systemd systems, etc.), not deeper into ~/.bashrc.
/etc/skel and how new accounts get these files
New home directories copy from /etc/skel when useradd creates a user
(unless overridden). On this Ubuntu install the skeleton looks like this:
ls -la /etc/skelOutput:
total 28
drwxr-xr-x 2 root root 4096 Apr 15 2025 .
drwxr-xr-x 145 root root 12288 Jun 9 13:33 ..
-rw-r--r-- 1 root root 220 Mar 5 2025 .bash_logout
-rw-r--r-- 1 root root 3771 Mar 5 2025 .bashrc
-rw-r--r-- 1 root root 807 Mar 5 2025 .profileNotice no .bash_profile here: that is normal for Debian-derived defaults,
while Red Hat family skel often ships .bash_profile instead. Always look
at /etc/skel on the distro you deploy, not at generic articles.
Hidden names are easy to miss when you inspect a home directory; ls -a is your
friend (show hidden files).
Order of execution for /etc/profile, ~/.bash_profile, and ~/.profile
For an interactive login Bash:
/etc/profile, if present.- Then exactly one of
~/.bash_profile,~/.bash_login,~/.profile— whichever exists first. - Later, when that login shell starts an interactive non-login child (for
example typing
bashwith no-l),~/.bashrcruns for the child.
Remote text logins are usually login shells; a tab in GNOME Terminal is often
non-login once the desktop session is up—hence the old confusion about “SSH ran
.bash_profile but this tab only ran .bashrc.”
SSH from another host is normally a login shell too,
unless your server forces something different.
Summary
Login shells read /etc/profile, then the first existing file among
~/.bash_profile, ~/.bash_login, and ~/.profile. Interactive non-login
Bash then reads ~/.bashrc (when your distro wires it that way). On Ubuntu,
~/.profile often sources ~/.bashrc for Bash logins, while ~/.bashrc
guards non-interactive shells. A ~/.bash_profile you add on Debian can hide
~/.profile entirely. Put long-lived exports on the login side, keep prompt and
alias noise in ~/.bashrc, and compare your changes to /etc/skel on the
distro you ship.
Further reading:

