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

Bashrc vs bash_profile and profile vs bashrc: when Bash reads ~/.bashrc, ~/.bash_profile, and ~/.profile on login and new terminals, Debian vs Red Hat patterns, and what /etc/skel ships for new users.

Published

Updated

Read time 5 min read

Reviewed byDeepak Prasad

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 creates a user (unless overridden). On this Ubuntu install the skeleton looks like this:

bash
ls -la /etc/skel

Output:

text
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 …