Skip to content

Instantly share code, notes, and snippets.

Last active August 29, 2015 14:06
Show Gist options
  • Save jgarber/20f56d92e549dbde7152 to your computer and use it in GitHub Desktop.
Save jgarber/20f56d92e549dbde7152 to your computer and use it in GitHub Desktop.
dotenv environments loading proposal

A proposal for loading multiple dotenv files

In regards to my comment on bkeepers/dotenv#120

Proposed heirarchy of environment variable sources

  1. ENV
  2. config/**/env
  3. .env
  4. config/**/env.#{Rails.env}
  5. .env.#{Rails.env}
  6. config/env.local
  7. .env.local
  8. config/env.#{Rails.env}.local
  9. .env.#{Rails.env}.local
  10. original ENV - merged last unless calling Dotenv.overload


Dotenv.load('.env', ".env.#{Rails.env}", ".env.local"...)

Result: Environments are merged in that order (last wins), but don't override existing ENV variables (which presumably come from the external environment)

Dotenv.overload('.env', ".env.#{Rails.env}", ".env.local"...)

Result: Environments are merged in that order (last wins), and any conflicting ENV variables are overridden.

Features of this scheme

  • The environment is the source of truth; dotenv is just there so you don't have to export a bunch of environment variables.
  • The base .env file contains most of your config. Additional Rails-environment-specific files tweak them per-environment (development, test, production). Your .env.local is meant to be gitignored and let you deviate from the shared config just for your machine.
  • One-off environment variables can be used to override the defaults set in your dotenv files (e.g. SOME_VAR=1 rails runner "puts ENV['SOME_VAR']" => 1).
  • If you do check in your .env files and then deploy to Heroku, it will still work because it preserves the environment set by Heroku. Your Heroku app vars will win and .env will just fill in any missing vars. This is an optimistic approach to managing your app configuration: the app doesn't blow up when you deploy but have forgotten to set a variable on Heroku.
  • Dotfiles are meant to be hidden, so they don't clutter up your view when you're looking at real files/folders. The config directory is a place to get config files out of the root of the app. Both serve similar purposes and this scheme lets you choose where you think developers will find your configs more easily: hidden in the root or visible in config/. Prefixing env files in config/ with a dot is obtuse.
  • The .local suffix is consistent with this dotfile convention

Implementation options

  1. Assume load will only be called once. .load merges all the .env files before applying the result to the environment.
  2. Save a copy of ENV to a module var before Dotenv ever touches ENV. Every time .load is called, re-merge that original environment last. This will give the most flexibility if someone wants to redefine the set of files provided to Dotenv.load in a Rails initializer, for example.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment