Document best practices
See original GitHub issueI struggle a long time to understand this 12-factors point about env-powered configurations.
I’ve read through all issues of this and the mirror Ruby repos. Through a number of stack overflow threads.
The final picture is still obscure to me. Maybe it’s really a wrong direction (if it causes so much confusion and debates between people) or maybe everyone needs a better explanation of the entire workflow.
I surely see a number of benefits in the env approach. Cross-language / cross-platform way of app configuration being the most vivid. But I also see drawbacks and I’m yet to find some comparison table which would ensure me that benefits are bigger in the common case.
Here are the questions from the top of my head:
-
Where to put default
.env
to copy at installation step? Should we exactly symlink or copy this file? -
If answer to 1) is copying: do you pass variable name to replace
NODE_ENV
value in destination.env
at installation step? -
How exactly do you set different
.env
for testing? At what place it occurs? There are multiple ways to implement this. Which one is recommended? -
Does the lack of global fallback values cause practical issues (your personal experience)?
process.env.xxx || <something>
is more or less a DRY violation… Most config systems provide an option to set global fallback value for every config option. Did anyone ended implementing his own custom solution to cover this case? -
Does string-only config cause practical issues (your personal experience)? Most config systems use js files with native types (and highlighting!) available. Did anyone ended implementing his own custom solution to cover this case?
-
Does the lack of a syntax for required config cause practical issues (your personal experience)? Most config systems provide this feature throwing exceptions in a “broken environment”. Did anyone ended implementing his own custom solution to cover this case?
-
Configuration in a frontend (client, browser)…
Personally, I find such blurry conventions undermine the whole “it’s more obvious” promo declaration. It’s hard to call something “superior” when it’s just about tradeoffs and personal preferences. I’d prefer to be wrong though.
[Added]: I already start to discover issues with 4) and 5). Most libraries expect boolean config values to be boolean. So we’re going to repeat not only default values but also type conversions (or string comparisons) in every place it will be used…
port = parseInt(process.env.HTTP_PORT) || 80 // in every place
...
port = parseInt(process.env.HTTP_PORT) || 80 // in every place
useEtag = process.env.HTTP_USE_ETAG == "true" ? true || false // in every place
...
useEtag = process.env.HTTP_USE_ETAG == "true" ? true || false // in every place
😞
Issue Analytics
- State:
- Created 8 years ago
- Reactions:4
- Comments:10 (3 by maintainers)
Thanks for the questions, Ivan. I’ll do my best to answer and maybe a wiki page can come of this thread.
You should put it in the root of your project. If you run your app as
node index.js
, your.env
file should be in the same directory asindex.js
. This is customizable with thepath
option, but by default, we look for it in the directory you’re executing node from.Either works. This is very dependent on your deploy process. Personally, we have used symlinks with a lot of success. We keep multiple releases so we can rollback bad deploys. We symlink the
.env
file for the project to each release directory. Here’s an example directory structure (truncated for clarity):We set this by hand in the
.env
file just like any other variable. Preparing the file is, again, very dependent on your deploy process. The sky is the limit for automation.I see an environment as being a combination of software and hardware (and that’s a very loose definition). If you’re running your app on your laptop, that is one environment. If you’re running tests on Travis-CI, that’s another environment. If you’re running tests on your laptop and want to use a different database for integration tests, I would suggest using a VM or Docker containers to separate. Trying to use the same environment (i.e. your laptop) as different environments (development and testing) is not a best practice.
No, having a misconfigured environment (incomplete
.env
), to me, is at the same level as not having your database running. Your app’s environment needs to be prepared to run and that includes a complete.env
file.Nope, I’ve never had a type issue. Passing string ports to express, hapi, sequelize, and mysql have worked just fine in my experience. I avoid boolean flags and instead opt for “only do this in production”
If you need to use boolean environmental variables, I’d recommend not setting it in your
.env
for environments where it would be false. Then, it will evaluate to false because it will not be set onprocess.env
I don’t understand this question. Will you elaborate?
First, be careful you’re are not sending sensitive information to the client. Stripe’s publishable key is a good example of a environmental-dependent value that you would load on the server and pass to the client.
script
tagPlease let me know if dotenv has ever referred to itself as “superior”, as that is not our intent, so I can correct it. TMTOWTDI when it comes to configuration. I’ve personally had a lot of success and enjoyment using dotenv. We’re always looking to improve but will also stick to our guiding principles.
Just to clarify the following question for future readers, which also confused me for a bit (and I think the README only lightly touched on):
“How do you deal with multiple .env files for dev, prod, etc.?”
This article clarified it. It basically says the reason you should only have one .env file is because Heroku, AWS, etc. should be managing your production variables for you. Your own .env file should only be for your local environment.
With that being said, lots of products are encouraging environment variables to be checked in to your repo in some form. Whether or not to do so is really a question about practicality and how much you want to adhere to the 12-factors. See Serverless or CircleCI for example.