Server Setup

CentOS/RHEL: where is my PATH?

by , , revisited on


We have by far the largest RPM repository with NGINX module packages and VMODs for Varnish. If you want to install NGINX, Varnish, and lots of useful performance/security software with smooth yum upgrades for production use, this is the repository for you.
Active subscription is required.

The PATH environment variable

For those unfamiliarly with what it is. The PATH environment variable controls which directories are being checked when you are attempting to run a program.
In other words, it helps construct the complete path to the program while searching for it across the specific directories.
Separated by a semicolon, the directories are listed in this variable’s value in the order of priority, with priority decreasing from left (highest) to the right (lowest) e.g.

PATH=/usr/local/bin:/usr/bin

The PATH customization per user

A Linux OS user can set up their own, user-specified PATH. This allows to easily launch user-installed programs. And to give priority between running different versions of the same program.

While doing my writeup on Faster PHP CLI and Cron Jobs, I have found quite a few inconsistencies about where you put the PATH environment variable customization for a user. Specifically for CentOS/RHEL systems.

Would you customize your PATH in the ~/.bashrc or ~/.bash_profile files?
And what it should be customized to initially?

&tldr; Scroll down to “The solution-workaround” section

The right ways:

  • The canonical and recommended approach is having PATH and its export in ~/.bash_profile only.
  • The canonical way of how things work is that ~/.bash_profile is being used for ssh user@example.com command invocations.

Unfortunately, none of this applies to CentOS/RHEL 7 and 8. Here’s why and what to do about it.

The defaults are quite different between CentOS/RHEL 7 and 8.

How CentOS 7 does it (wrong)

A new user gets PATH added to ~/.bash_profile and it’s set to

PATH=$PATH:$HOME/.local/bin:$HOME/bin

So initially it is /sbin:/bin:/usr/sbin:/usr/bin:/usr/local/sbin:/home/foo/.local/bin:/home/foo/bin

Setting PATH in ~/.bash_profile is correct because it prevents “double-sourcing” PATH. If it was put to ~/.bashrc, running subshell, e.g. echo $(command), would source the file many times, and this would result in a lengthy PATH with duplicate directories for the subshells.

However, $PATH:$HOME/.local/bin:$HOME/bin is not good in case we want to override system programs with user’s installed programs or custom wrappers, like in the Faster PHP CLI and Cron Jobs.

But the other “bad” thing, if you will, is that running non-interactive commands over SSH, will not include our user-specific PATH, e.g.

ssh user@example.com command

will not have ~/.local/bin included, because only ~/.bashrc is used for such a case!

How CentOS 8 does it (“work in progress”)

CentOS/RHEL 8 had a history of changes in the way custom user PATH was included to either ~/.bashrc or ~/.bash_profile.

It was present in 8.0 in ~/.bash_profile, then in ~/.bashrc in subsequent versions. Seems that this is the very part where developers didn’t make up their mind.
At one point between those changes, double-sourcing PATHs was possible, which means that subshells would see this PATH:

/sbin:/bin:/usr/sbin:/usr/bin:/usr/local/sbin:/home/foo/.local/bin:/home/foo/bin/sbin:/bin:/usr/sbin:/usr/bin:/usr/local/sbin:/home/foo/.local/bin:/home/foo/bin

Or even lengthier, depending on the nesting.

The latest versions of CentOS/RHEL 8 almost got it right, except that command over ssh still do not source ~/.bash_profile, and source ~/.bashrc instead, for whatever reason.
So it gets “work in progress” label from me.

What you see is what you got

Worth noting that how your specific ~/.bashrc or ~/.bash_profile looks now depends on the way skeleton files were at the time your user was created.
You can find the skeleton files, in e.g. /etc/skel/.bash_profile.

The solution-workaround

In most situations, we:

  • want commands run via ssh user@example.com command to use our custom `PATH.
  • don’t want to double-source our PATH statement causing duplication
  • want our PATH to use the customization for interactive SSH sessions

So the solution is non-intuitive and non-standard and follows up pretty much with the latest RHEL 8 thinking. Set your custom path in ~/.bashrc only

# User specific environment
if ! [[ "$PATH" =~ "$HOME/.local/bin:$HOME/bin:" ]]; then
    PATH="$HOME/.local/bin:$HOME/bin:$PATH"
fi
export PATH

The condition prevents double-sourcing, even though we use the non-canonical ~/.bashrc location for our PATH customization.

In this way, finally, things at least will work the right way and as expected.

Leave a Reply

This site uses Akismet to reduce spam. Learn how your comment data is processed.

%d bloggers like this: