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
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.
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
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