The Environment is a concept that was introduced by Version 7 Unix. It stores keys and their corresponding values and makes this available to processes. Since V7 Unix, Linux, MacOS and Windows have all implemented environments. If you want to read up on how the environment works in Linux, you can use the man pages: man 7 environ to get started. I quote:

The variable environ points to an array of pointers to strings called the “environment”.

and:

By convention the strings in environ have the form “name=value”.

Environment variables may seem simplistic or useless, but far from it.

Why does this matter?

Many programs alter their behavior based on the value of environment variables. For example, Flatpak troubles arise when the XDG environment variables have not been set correctly.

Using environment variables this way can be convenient, as it allows the programmer to provide flexibility through variables which the user can easily set just before running the program. The user enjoys quickly changing the behavior of the program simply by modifying the environment variables, e.g.:

LOG_LEVEL=debug command

This command sets the value of LOG_LEVEL to debug for the scope of command - after which LOG_LEVEL=debug is removed from the environment. Using this strategy, we can quickly iterate, or apply the same program to different use cases.

Another popular use is to allow web servers to allocate ports based on a PORT environment variable.

Makefiles are also built with environment variables in mind, where variables are given a default value, which can be overridden by the environment.

So what’s the problem?

A problem arises when programs start to depend on environment variables which aren’t necessarily set properly. For example, IntelliJ products behave incorrectly in tiling window managers, unless an obscure variable is set: _JAVA_AWT_WM_NONREPARENTING=1. Firefox currently requires MOZ_ENABLE_WAYLAND=1 to run natively in the Wayland protocol, as opposed to X11.

Now, as man 7 environ explains, there are several places where you can define environment variables which will be read as the user logs in. Places such as /etc/environment, or /etc/profile are useful for variables which you want to be set in all you sessions.

But what if you need variables for the specific desktop environment or window manager that you choose, and none other?

What if you switch between XMonad, LeftWM, i3-gaps, Gnome and Sway? You need different environments depending on your choice of display protocol (X11 vs. Wayland), and depending on your window manager (typically tiling vs. stacking), and a whole host of details which may be specific to the programs you use.

The Solution

The solution, obviously, is indirection!

“All problems in computer science can be solved by another level of indirection”. - David Wheeler

Kenny Levinsen explained to me how he uses an indirection script to export the necessary variables before launching the target program. For example, in order to run sway, he would call sway-run.sh:

#! /bin/sh

# Export the necessary variables
export VAR1=val1
export VAR2=val2

# Launch sway with the same parameters that this script was called
exec sway "$@"

Additionally, you could source scripts depending on other factors, such as whether you’re launching in X11 or Wayland. Creating indirection scripts like this for each of your sessions is an excellent way to keep track of all of your environment variables.

Now, while I am a big fan of greetd, which allows you to launch arbitrary commands on log-in, I recognize that most display managers use .desktop files to manage how the session is started. For those, you will have to edit the desktop files such that they call your indirection (aka wrapper) scripts instead of the binaries themselves.

If we continue using Sway as an example, you’ll want to edit Sway.desktop such that it calls sway-run.sh instead of sway.

On Doing One Thing Well

I think this is a really attractive solution, because it places the responsibility of establishing environment variables in a clear location, and makes it rather straightforward. One of the most satisfying things as a program author is when you realize that you can safely not implement something in your program, knowing that it has already been taken care of elsewhere. For window managers and desktop environments, I believe this is one of those cases.

Many people state that one of the fundamental Unix philosophies is to make programs that do one thing, and do it well. While this may not always be the case, it is nice to know that we can let our WMs and DEs do their job, and we’ll worry about the environment elsewhere.

Sidenote: my dotfiles are littered with attempts to set environment variables correctly, most of which didn’t work.