Bashrc vs bash_profile: login vs non-login shells, .profile, and /etc/skel

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 bash from 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 ~/.bashrc by default. Optional setup goes through BASH_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/skel setups; often ends with source ~/.bashrc so login shells still pick up aliases and prompt tweaks.
  • .profile: traditional login file shared with sh. On Debian / Ubuntu, new users typically get ~/.profile (not ~/.bash_profile) and that file sources ~/.bashrc when the shell is Bash—so .profile vs .bashrc is “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:

text
# if running bash
if [ -n "$BASH_VERSION" ]; then
    # include .bashrc if it exists
    if [ -f "$HOME/.bashrc" ]; then
	. "$HOME/.bashrc"
    fi
fi

Stock ~/.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 *_HOME variables—in ~/.profile or ~/.bash_profile (whichever your distro actually uses for login), or in /etc/profile.d/*.sh for machine-wide policy.
  • Put interactive-only settings—PS1, many alias / bind entries, shopt, completion hooks—in ~/.bashrc, guarded by the usual case $- in *i*) … *) return;; esac block 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 command creates a user (unless overridden). On this Ubuntu install the skeleton looks like this:

bash
ls -la /etc/skel

Output:

output
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 .profile

Notice 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:

  1. /etc/profile, if present.
  2. Then exactly one of ~/.bash_profile, ~/.bash_login, ~/.profile — whichever exists first.
  3. Later, when that login shell starts an interactive non-login child (for example typing bash with no -l), ~/.bashrc runs 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:

Deepak Prasad

R&D Engineer

Founder of GoLinuxCloud with more than 15 years of expertise in Linux, Python, Go, Laravel, DevOps, Kubernetes, Git, Shell scripting, OpenShift, AWS, Networking, and Security. With extensive experience, he excels across development, DevOps, networking, and security, delivering robust and efficient solutions for diverse projects.

  • Go (programming language)
  • Python (programming language)
  • DevOps
  • Computer Security
  • Cloud Computing
  • Kubernetes
  • Linux
  • Ansible (software)