Default `PATH` hierarchy: Homebrew and user binaries

Well, first of all: :bowing_man: – Bluefin is a dream come true!

While getting accustomed with all those immutability features/oddities, I stumbled upon a quite annoying issue, at least for newcomers:

Running the bare gsettings[1] command just didn’t reflect my Gnome desktop settings! Whenever I changed a setting via gsettings set <key> <value>, it had no effect on my desktop. And settings changed via the GUI weren’t reflected in gsettings get <key>.

This confused me so much that I even seeked the attention of a Gnome dev – and wasted some of their time.

As I figured out today, Homebrew knows its own gsettings binary (probably pulled as a dependency of some tool I installed, I dunno). In combination with Bluefin’s default PATH hierarchy of

/home/linuxbrew/.linuxbrew/bin:/home/linuxbrew/.linuxbrew/sbin:/var/home/salim/.local/bin:/var/home/salim/bin:/usr/lib64/ccache:/usr/local/bin:/usr/local/sbin:/usr/bin:/usr/sbin

this lead to

> which gsettings
/home/linuxbrew/.linuxbrew/bin/gsettings

:exploding_head:

While I understand that Homebrew’s binaries generally are supposed to take precedence over the immutable system ones in /usr/bin/ & Co., I’m wondering:

  1. Why does Homebrew’s bin paths come before (i.e. mask) the user’s bin paths (~/.local/bin and ~/bin)? This seems just wrong to me.

  2. Is there an easy way to stop Homebrew from polluting my PATH (i.e. masking arbitrary system binaries) with transitive dependencies like the gsettings CLI[2] without altering the general PATH hierarchy?

    Or should we actually move Homebrew’s binary dirs at the very end of PATH since we’re running an immutable OS anyways[3]?


tl;dr: If you wanna change your desktop’s settings via the commandline, always use /usr/bin/gsettings!


  1. This links to the Debian manpage, which isn’t completely accurate for our Fedora builds. But debiman renders manpages in such a perfect way that it’s hard to resist linking to them. ↩︎

  2. Which doesn’t even exist as a formula itself. ↩︎

  3. Which only ships a carefully selected palette of CLIs. ↩︎

Look in /etc/profile.d/brew.sh if you want to mess with the order.

  1. That’s what it does by default.
  2. Yeah it would be nice if we could find a way to do this. Similarly if you pull in something with systemd you’ll have a systemctl command in brew on the system, which is also bad. There’s probably a good list of things we could denylist.

It might be worth flipping the path to see how that works, though I suspect something else would break lol. I noticed this on my system as well:

❯ which gsettings
/home/linuxbrew/.linuxbrew/bin/gsettings

I guess that’s been there this whole time but I stopped doing runtime customization long ago. :smile: It would be great if someone found some time to dig into this and make a recommendation.

1 Like

There’s probably a good list of things we could denylist.

Although such a denylist sounds a lot like the poorly scaling practice of enumerating badness, I’m afraid it’d be the only sane option if we wanna stick with Homebrew.

At least that’s my impression after reading up on this earlier Bluefin issue where Homebrew’s bins broke Flatpaks. This specific issue has been “fixed” (or rather worked around) by only adding Homebrew’s bin dir to PATH in interactive shells.

It might be worth flipping the path to see how that works, though I suspect something else would break lol.

I think you already answered this some while ago in the aforementioned issue: Flipping the PATH

(…) solves the problem, but then the issue gets flipped, if something installed via brew installs a dependency that also happens to be on the host, the host’s version will take precedence, which could be a problem.

Since Homebrew behaves like a kind of “centre of the universe” – i.e. doesn’t care about system binaries and at the same time has no concept of isolation – a denylist approach seems the only way out[1].


  1. I sincerely hope I’m mistaken and someone more proficient comes up with a real solution to the problem… :sweat_smile::crossed_fingers: ↩︎

1 Like

Conceptually speaking, the allowlist approach (enumerating goodness) would be to install homebrew in a distrobox container in order to isolate it, and only distrobox export selected binaries from the distrobox container, right? I imagine there’s an important reason I’m forgetting about why we used to do that but then moved to just running homebrew on the host…after reviewing Bluefin is feature complete - #6 by j0rge , it seems like the initial reasoning at the time for going back the homebrew on the host (after the discovery of the workaround to only change PATH in interactive shells) was to keep the UX and system architecture simpler to understand?

1 Like

Yeah. I tried brew-in-a-distrobox the other day and I couldn’t stand it haha. I think maybe some best practices to put in the docs will get us there, it’s been a long time and we’re only seeing this one now so it shouldn’t be hard for me to toss it into the docs this weekend.

(Also what is gsettings even doing in brew to begin with lol that makes no sense to me.)

That sounds like a sane approach to me, actually. As long as the distrobox export could be made automatic based on what the user really intended to install (and thus expose to the host), rather than all of the (transitive) dependencies brew pulls in.

I don’t think the current situation is particularly simple to understand… :sweat_smile: IMHO, it’s not at all clear to the user what (transitive) dependencies get pulled in – masking commands on the host – when they run a simple brew install TOOL.

Try brew install netlify-cli…

Spoiler
==> Installing dependencies for netlify-cli: cfitsio, cgif, liblqr, jasper, libraw, imagemagick, libexif, libaec, pkg-config, hdf5, libmatio, gdk-pixbuf, librsvg, libspng, mozjpeg, uthash, libdicom, openslide, libassuan, libksba, systemd, libusb, npth, libsecret, pinentry, gnupg, gpgme, nspr, nss, poppler, vips and xsel 

Currently, my Bluefin gives

brew list
==> Formulae
age           ca-certificates    erdtree     gh         img2pdf libb2        libmatio        libssh2      libxfixes mpfr           openssl@3        pycparser          srt vips
alsa-lib       cairo        expat        ghostscript  isl libbluray        libmicrohttpd   libtasn1      libxi mpg123        opus            python-packaging  svt-av1 webp
aom           certifi        eza        giflib     jasper libcap        libmpc        libtiff      libxinerama ncurses       orc            python@3.12       syncthing        x264
apr           cffi        fd        git-sizer     jbig2dec libde265        libnghttp2        libtirpc      libxml2 neonctl       p11-kit            qpdf          systemd x265
apr-util       cfitsio        ffmpeg        glab     jbig2enc libdicom        libnsl        libtool      libxrandr netlify-cli   pandoc            rav1e          taplo xinput
argon2           cgif        fftw        glib     jpeg-turbo libedit        libogg        libunibreak   libxrender nettle        pandoc-crossref        readline tcl-tk           xorgproto
aribb24        cjson        flac        gmp         jpeg-xl libevent        libpng        libunistring  libxscrnsaver nixpacks      pango            ripgrep          tealdeer xsel
aspell           cryptography    flyctl        gnupg     jq libexif        libpq        libusb      libxslt     node pcre2            rtmpdump          tesseract        xvid
ast-grep       curl        fnm        gnutls     krb5 libffi        libraqm        libuv      libxv         npth perl            rubberband          theora           xz
atuin           dart        fontconfig  go         ladspa-sdk libgcrypt        libraw        libvidstab      libxxf86vm nspr           phoronix-test-suite  rustup          tidy-html5 yq
autoconf       dasel        freetds     gpgme     lame libgit2        librespeed-cli  libvmaf      libzip     nss php            s5cmd          ugrep           zeromq
b2-tools       dav1d        freetype    graphite2     lazydocker libgit2@1.7    librist        libvorbis      little-cms2 ocrmypdf      pillow            sass          unbound           zimg
bash           dbus        frei0r        harfbuzz     lazygit libgpg-error   librsvg        libvpx      lz4         ollama pinentry            sd              uni           zlib
bat           device-mapper    fribidi     hdf5     leptonica libheif        libsamplerate   libx11      lzo         oniguruma pixman            sdl2          unixodbc           zoxide
berkeley-db@5  diceware        fzf        highway     libaec libice        libsecret        libxau      m4         open-mpi pkg-config        shared-mime-info  unpaper           zstd
binutils       diffr        gcc        hugo     libaio libidn        libsndfile        libxcb      mawk opencore-amr  pmix            shellcheck          unzip
brotli           direnv        gd        hwloc     libarchive libidn2        libsodium        libxcrypt      mbedtls openexr       pngquant            snappy          uthash
buf           dive        gdbm        icu4c     libass libimagequant  libsoxr        libxcursor      micro openjpeg      poppler            speex          util-linux
bzip2           dnsping        gdk-pixbuf  imagemagick  libassuan libksba        libspng        libxdmcp      mozjpeg openldap      pulseaudio        speexdsp          uv
c-ares           dysk        gettext     imath     libavif liblqr        libssh        libxext      mpdecimal     openslide pybind11            sqlite          vamp-plugin-sdk 

ls -1 /home/linuxbrew/.linuxbrew/bin

ls -1 /home/linuxbrew/.linuxbrew/sbin

(output of the last two commands exceeded Discourse’s max allowed chars per post, hence the links)

So I guess gsettings appears to be the least of the worries, compared to the stuff from the util-linux cellar… :dizzy_face:

Shoot, I’m still not sure if I should be using Hombrew on the host or the bluefin-cli… or how to know when I would want to use one or the other of those!

The docs don’t mention the container, you only need that if you’re looking for it and want it.

Ok, cool. I have kind of been treating brew on the host as the “default” for things I want system-wide. Stuff I need in a particular container for development purposes… I install in that container.

1 Like