direnv

All of our services follow the 12-factor methodology. This post is only about one factor in particular — configuration via environment variables, and specifically about the underrated direnv, which automatically loads environment variables when you cd into a project directory.

The shared configuration of our projects lives in .env and .env.local files, where the latter isn’t checked into source control and is used to override settings in the local environment.

As always, to keep tool versions identical across every developer’s machine, I recommend installing direnv via asdf:

asdf plugin add direnv
asdf install direnv latest

Inside the project directory, pin the version:

asdf local direnv latest

Don’t forget to add the initialization line to your shell’s rc file:

eval "$(direnv hook zsh)"

# and restart the shell
exec $SHELL

What’s left is configuring the project itself. direnv reads .envrc, and to make our .env / .env.local setup work automatically, create an .envrc at the root of the project with the following contents:

dotenv
dotenv_if_exists .env.local

The first directive loads .env. The second loads .env.local only if it exists.

Once everything is set up, don’t forget to authorize the automatic loading of environment variables by running the following command at the project root:

direnv allow

The main advantage over the alternatives, in my opinion, is the automatic “unloading” of variables when you leave the project folder. That helps avoid nasty side effects, especially when different templated projects use the same variable names.

References