Managing environment variables with direnv
Following the Twelve-Factors app, most web applications are storing configuration in environment variables rather than in a repository. It is possible to store secrets securely in a repository with git-crypt but that will likely be the topic of another article.
While in CI and on servers loading those environment variables happen automatically, loading them on your computer is problematic. The
most popular approach is dotenv
: you store your environment variables in a .env
file and a library in your project will look for
it and load them. A .env
file sample:
REDIS_URL=blabla
# Make sure this is not used in other environments
APP_SECRET=hunter2
TRIAL_DAYS_LENGTH=14
And, for example in the JavaScript implementation, they can be loaded by adding require('dotenv').config()
to your project, as early as possible.
As you can see, this adds a dependency to your project that is only used in development: in CI variables are likely to be set in the CI itself and in production
by whatever provisioning tool you are using.
Enter direnv.
It starts the same way as dotenv
: you write your variables in a .envrc
file which is almost identical to the example above: entries have an export
keyword first.
export REDIS_URL=blabla
# Make sure this is not used in other environments
export APP_SECRET=hunter2
export TRIAL_DAYS_LENGTH=14
What sets it apart is how it loads the variables.
You first need to install direnv
, which is a cross-platform binary available on most package managers as well as set up a hook in your bash/zsh/fish config.
This might sound complex but you only need to do this setup once per computer and it actually takes less than a minute. The hook is fast enough that it
is imperceptible - no need to worry about terminal slowdown.
Now that you have the direnv
hook, if you cd
to a directory with a .envrc
you will see:
$ direnv: error .envrc is blocked. Run `direnv allow` to approve its content.
As mentioned in the message, direnv
will not automatically load the variables found: you first need to allow the .envrc
file. You will need
to run direnv allow
every time the .envrc
is modified. Since direnv
loads the variable automatically, this is needed for security as any
repo could run commands on your machine otherwise.
And that's it you're done. Every time you will cd
in a directory, it will automatically load variables found in .envrc
and unload them when moving
out of the directory:
$ cd my-repo/
direnv: loading .envrc
direnv: export +AWS_ACCESS_KEY_ID +AWS_SECRET_ACCESS_KEY +RUST_LOG +SENTRY_DSN
$ cd ..
direnv: unloading
You get the benefits of environment variables for configuration without having to add a dependency to your project.
I was actually using direnv
before hearing about dotenv
: https://github.com/Keats/dbmigrate/pull/14 and I still don't really see myself using dotenv
over it.
I'm guessing the advantage of dotenv
is if you are launching things from an IDE directly, using Docker or at companies restricting what can be installed on a machine.
If this is not your case, I highly recommend direnv
.