Sign in
Topics
Build and launch your apps in minutes
Learn the best practices of using npm dotenv, know how it manages environment variables in Node.js. Load configuration from a
.env
file to keep sensitive keys safe, work with multiple environments, and simplify project setup without exposing credentials in your code.
Hard-coding API keys or database passwords into your code invites security issues. npm dotenv solves this by loading environment variables from .env
file, keeping sensitive data separate from your application code. This guide explains how to set it up and manage environment-specific variables across different environments.
One of the simplest ways to load environment variables into your application code is by using the zero-dependency dotenv
module from npm.
When your app starts, you can preload dotenv using:
1node -r dotenv/config your_script.js 2
This loads environment variables from a .env
file into process.env
before your script runs. The result: your app gets a clean set of environment-specific values without hard-coding sensitive data like API keys.
What’s the use of dotenv?
It lets you separate configuration from your code, following the Twelve-Factor App principle. A .env
file stores environment variables in your project’s root directory. This makes it safer to store secrets—like API keys, database passwords, or production-specific configs—outside version control.
First, install the module:
1npm install dotenv 2
Should you install as a dev dependency?
If you need dotenv in both development and production (e.g., loading DB credentials in both environments), install it as a normal dependency. Only use --save-dev
if it’s strictly for local development.
In your application code (CommonJS syntax):
1const dotenv = require('dotenv'); 2dotenv.config(); 3
Or, in ES modules:
1import dotenv from 'dotenv'; 2dotenv.config(); 3
Here, calling dotenv.config()
parses your .env
file and injects the variables into process.env
.
Tip: If you’re using ECMAScript Modules (ESM) in Node.js (i.e., import instead of require), then:
You must call
dotenv.config()
before importing any other module that uses environment variables. Otherwise, those modules will load before your environment is set andprocess.env
will be empty in those files. —- LinkedIn Post
You can tell dotenv to load a different env file without modifying your code:
1# Preload dotenv before your script 2node -r dotenv/config your_script.js 3 4# Preload dotenv with a custom env file path 5DOTENV_CONFIG_PATH=./custom.env node -r dotenv/config your_script.js 6
This is ideal for managing environment-specific variables (e.g., .env.development
, .env.production
).
dotenv.config()
returns an object with:
parsed
— the loaded key/value pairserror
— any error that occurred during parsingYou can reference one variable inside another by also using dotenv-expand
:
.env
1BASE_URL=http://localhost 2API_URL=${BASE_URL}/api 3
index.js
1import dotenv from 'dotenv'; 2import dotenvExpand from 'dotenv-expand'; 3 4const myEnv = dotenv.config(); 5dotenvExpand.expand(myEnv); 6
Multiline variables are also supported, making it safe to store private keys without breaking parsing. If things don’t load as expected, you can enable debug output:
1DEBUG=true node -r dotenv/config your_script.js 2
Want to manage your projects even faster? Try Rocket.new — instantly create, configure, and launch ready-to-code apps with dotenv pre-setup.
A typical workflow might load development database credentials locally, and production credentials on a server by pointing to a different .env
file. For example:
1# Development 2DOTENV_CONFIG_PATH=.env.development node -r dotenv/config app.js 3 4# Production 5DOTENV_CONFIG_PATH=.env.production node -r dotenv/config app.js 6
This separation keeps secrets out of your code and version control.
1const path = require('path'); 2const dotenv = require('dotenv'); 3 4const result = dotenv.config({ 5 path: path.resolve(__dirname, '.env'), // default is .env in project root 6 encoding: 'utf8', 7 debug: process.env.DEBUG === 'true' 8}); 9 10if (result.error) { 11 console.error('Error loading .env:', result.error); 12} else { 13 console.log('Parsed keys:', Object.keys(result.parsed)); 14} 15
Once dotenv is configured, variables are available via process.env
:
1console.log(process.env.NODE_ENV); 2console.log(process.env.API_KEY); 3
This allows you to keep configuration outside of code—especially useful in cloud deployments, where env vars can be set without editing source files.
node -r dotenv/config
in production and development..env.development
, .env.test
, .env.production
.DOTENV_CONFIG_PATH
before running your app..env
HELLO=hello world
GREETING="hello world again"
index.js
1import dotenv from 'dotenv'; 2dotenv.config(); 3 4console.log(process.env.HELLO); // hello world 5console.log(process.env.GREETING); // hello world again 6
Both quoted and unquoted values work fine.
Using dotenv to manage environment variables is straightforward, but applying best practices ensures security, maintainability, and scalability in different environments (development, staging, production). Below are the key recommendations:
.env files often contain sensitive information like API keys, database credentials, or private tokens.
Always add .env
to your .gitignore
file:
1echo ".env" >> .gitignore 2
Instead of committing the file, share it securely with your team through password managers or secure configuration services.
Maintain separate files like:
1.env.development 2.env.staging 3.env.production 4
Specify the correct file in dotenv.config():
1require('dotenv').config({ path: '.env.production' }); 2
This avoids overwriting production settings with development values.
Prefix variables with the project or service name to avoid conflicts.
1APP_DB_HOST=localhost 2APP_DB_USER=root 3
This becomes especially important when multiple projects run on the same machine or server.
Avoid nesting structures in .env
files — dotenv does not parse objects or arrays natively.
Example:
API_URL=https://api.example.com
API_TIMEOUT=5000
Use a validation library (like envalid or joi ) to ensure all required environment variables are present and correctly formatted.
1const envalid = require('envalid'); 2const { str, num } = envalid; 3 4envalid.cleanEnv(process.env, { 5 API_URL: str(), 6 API_TIMEOUT: num({ default: 5000 }) 7}); 8
Maintain a .env.example
file containing all keys without sensitive values:
API_URL=
API_KEY=
DATABASE_URL=
This helps with onboarding new developers and avoids “it works on my machine” issues.
require('dotenv').config()
at the very top of your entry file (before importing any module that depends on environment variables).If a default is needed, set it in .env or handle it gracefully:
1const port = process.env.PORT || 3000; 2
.env
files.In long-running processes, environment variables are loaded only at startup. If you change .env mid-run, you’ll need to restart the app to apply updates.
Now you know how to:
.env
Set up .env
for development and production, enable debug mode if keys fail to load, and enjoy a safer, more maintainable configuration system.