I write software locally, push it to remote Git server, where if the tests pass it gets deployed to staging environment. If the staging environment works correctly, then I will deploy the software to production. I often use NODE_ENV
environment variable to flag these three environments. By default, the environment variable is unset and defaults to development
1 | process.env.NODE_ENV = process.env.NODE_ENV || 'development' |
Depending on the NODE_ENV
my program could load different settings: urls, logging parameters, server routes. Often, it is a YAML or a JSON file with environment names as keys
1 | { |
When running on staging or production, I set NODE_ENV
variable on the server to staging
or production
. This value then lets my code load right config for the environment. If NODE_ENV=production
then npm install
and npm ci
install production NPM dependencies. So there is a catch:
- On staging with
NODE_ENV=staging
npm install
andnpm ci
will install production AND dev dependencies - Thus staging will NOT be exactly like production.
Staging would run just fine if one of the dependencies was saved as dev dependency by mistake, but the same application would crash in production because that dependencies would not be present.
Modifying npm install
call with conditional to add --production
flag when running on staging and production would create a nasty shell command. Luckily NPM thought about this. There is an additional environment variable we can set to install only the production dependencies on staging - it is NPM_CONFIG_PRODUCTION
which acts just like --production
during install step.
But watch out! Setting NPM_CONFIG_PRODUCTION=true
during install overrides NODE_ENV
for all npm scripts, which is what NPM intended. So the server will behave differently if you call node ./start.js
or npm start
.
1 | NPM_CONFIG_PRODUCTION=true |
Server starts with process.env.NODE_ENV=staging
value. But if you have NPM script start
that does the same in package.json
the result will be different.
1 | { |
1 | NPM_CONFIG_PRODUCTION=true |
Server starts with process.env.NODE_ENV=production
value!
We got burnt by this once - good thing we have noticed error reports from staging
being written to the production dashboard, and figured why the staging server was running against production before any production data was corrupted.
There are two solutions.
1. Override process.env.NODE_ENV
in every entry point with a different variable like FORCE_NODE_ENV
1 | process.env.NODE_ENV = |
You have to be 100% sure that every script - start.js
, knex.js
, db/migrations.js
goes through the same override first. Otherwise some script might still be executed against production, which is ... less than ideal.
2. Use another variable to pick the environment settings, and leave NODE_ENV
alone. For example, the variable SETTINGS
could be development
, staging
or production
, and NODE_ENV
will be always be undefined or production
.
1 | process.env.SETTINGS = process.env.SETTINGS || 'development' |
The second method is my preferred one - but it might not be supported by every config-loading library. So find a config library that does allow you to specify a different variable from NODE_ENV
. For example config allows using NODE_CONFIG_ENV to specify environment to load.