How to Use Dotenv in Nodejs

How to Use Dotenv in Node.js Managing configuration settings in Node.js applications can quickly become a chaotic and error-prone task—especially as projects grow in complexity. Hardcoding sensitive data like API keys, database credentials, or environment-specific URLs directly into your source code is a serious security risk and violates modern development best practices. This is where dotenv com

Oct 30, 2025 - 13:04
Oct 30, 2025 - 13:04
 0

How to Use Dotenv in Node.js

Managing configuration settings in Node.js applications can quickly become a chaotic and error-prone taskespecially as projects grow in complexity. Hardcoding sensitive data like API keys, database credentials, or environment-specific URLs directly into your source code is a serious security risk and violates modern development best practices. This is where dotenv comes in as an essential tool for Node.js developers.

Dotenv is a zero-dependency module that loads environment variables from a .env file into process.env. It simplifies configuration management by allowing developers to store sensitive and environment-specific data in a separate, ignored filekeeping codebases clean, secure, and portable across different environments like development, staging, and production.

In this comprehensive guide, youll learn exactly how to use dotenv in Node.jsfrom installation and basic usage to advanced patterns, real-world examples, and industry best practices. Whether youre building a REST API, a full-stack application, or a microservice, mastering dotenv will improve your applications security, maintainability, and scalability.

Step-by-Step Guide

Step 1: Install Dotenv

The first step to using dotenv is installing it via npm or yarn. Open your terminal, navigate to your Node.js project directory, and run one of the following commands:

npm install dotenv

or

yarn add dotenv

This installs the dotenv package as a production dependency since its required for your application to run in any environment. Unlike development-only tools (like nodemon), dotenv is essential for proper configuration loadingeven in production, as long as environment variables are properly managed.

Step 2: Create a .env File

Next, create a file named .env in the root directory of your project. This file will contain key-value pairs representing your environment variables. Each line should follow the format:

KEY=VALUE

For example, heres what a typical .env file might look like:

PORT=3000

NODE_ENV=development

DB_HOST=localhost

DB_PORT=5432

DB_NAME=myapp_dev

DB_USER=admin

DB_PASSWORD=supersecretpassword123

JWT_SECRET=myjwtsecretkeythatshouldbe32characterslong

API_KEY=abc123xyz987

BASE_URL=https://api.example.com

Important notes:

  • Do not use spaces around the = sign.
  • Values can be wrapped in quotes if they contain spaces or special characters: API_KEY="abc 123 xyz"
  • Comments are not supported in .env files. Avoid using

    or // for annotations.

  • Always use uppercase for variable namesits a widely accepted convention.

Step 3: Load Environment Variables in Your App

To load the variables from your .env file, you must require and configure dotenv at the very top of your main application filetypically server.js, app.js, or index.js.

Heres how:

require('dotenv').config();

console.log(process.env.PORT); // Output: 3000

console.log(process.env.DB_HOST); // Output: localhost

Its critical that require('dotenv').config(); is called before any other code that relies on environment variables. If you import a configuration module or database connection file before loading dotenv, those files will read undefined values from process.env, leading to runtime errors.

Alternatively, if youre using ES6 modules (i.e., "type": "module" in package.json), use the import syntax:

import dotenv from 'dotenv';

dotenv.config();

console.log(process.env.PORT);

Step 4: Use Environment Variables in Your Application

Once dotenv has loaded the variables, you can access them anywhere in your application using process.env.VARIABLE_NAME.

Heres a practical example using Express.js:

const express = require('express');

const app = express();

require('dotenv').config();

const PORT = process.env.PORT || 5000;

const DB_HOST = process.env.DB_HOST;

const DB_PASSWORD = process.env.DB_PASSWORD;

app.get('/', (req, res) => {

res.send('Server is running!');

});

app.listen(PORT, () => {

console.log(Server running on http://localhost:${PORT});

});

In this example, the server will listen on port 3000 (as defined in .env). If PORT is not defined in .env, it defaults to 5000. This fallback mechanism is useful for development but should be avoided for critical variables like database credentials.

Step 5: Configure Your Database Connection

One of the most common use cases for dotenv is managing database connection strings. Heres an example using the PostgreSQL client pg:

const { Pool } = require('pg');

require('dotenv').config();

const pool = new Pool({

user: process.env.DB_USER,

host: process.env.DB_HOST,

database: process.env.DB_NAME,

password: process.env.DB_PASSWORD,

port: process.env.DB_PORT,

});

pool.query('SELECT NOW()', (err, res) => {

console.log(err, res);

pool.end();

});

By externalizing credentials, you avoid exposing them in version control and make it easy to switch between different databases for different environments.

Step 6: Use Dotenv in Multiple Environments

Real-world applications often require different configurations for development, testing, and production. Dotenv supports this through multiple .env files.

Create separate files:

  • .env.development
  • .env.production
  • .env.test

Each file contains environment-specific values:

.env.development

PORT=3000

NODE_ENV=development

DB_HOST=localhost

DB_PASSWORD=devpass123

.env.production

PORT=8080

NODE_ENV=production

DB_HOST=prod-db.example.com

DB_PASSWORD=prodsecretpassword!

To load a specific environment file, pass the path option to config():

require('dotenv').config({ path: .env.${process.env.NODE_ENV} });

Now, when you start your application with:

NODE_ENV=production node server.js

Dotenv will automatically load .env.production. If the file doesnt exist, it falls back to .env.

Step 7: Integrate with Package.json Scripts

For convenience, define scripts in your package.json to set environment variables before running your app:

{

"scripts": {

"start": "node server.js",

"dev": "NODE_ENV=development node server.js",

"prod": "NODE_ENV=production node server.js",

"test": "NODE_ENV=test jest"

}

}

On Windows, the above syntax may not work. Use the cross-env package for cross-platform compatibility:

npm install cross-env --save-dev

Then update your scripts:

{

"scripts": {

"dev": "cross-env NODE_ENV=development node server.js",

"prod": "cross-env NODE_ENV=production node server.js",

"test": "cross-env NODE_ENV=test jest"

}

}

Now you can run npm run dev or npm run prod safely on any operating system.

Step 8: Validate Environment Variables

Its good practice to validate that required environment variables are present before starting your application. Otherwise, your app may crash unexpectedly in production.

Create a simple validation utility:

require('dotenv').config();

const requiredEnvVars = ['DB_HOST', 'DB_USER', 'DB_PASSWORD', 'JWT_SECRET'];

const missingVars = requiredEnvVars.filter(varName => !process.env[varName]);

if (missingVars.length > 0) {

console.error('Missing required environment variables:', missingVars);

process.exit(1);

}

console.log('All required environment variables are set.');

You can place this validation at the top of your main server file. This ensures your application fails fast with a clear error message if critical variables are missing.

Best Practices

Never Commit .env to Version Control

Your .env file contains sensitive information and should never be tracked by Git or any other version control system. Add it to your .gitignore file:

.env

.env.local

.env.*.local

This prevents accidental exposure of credentials if your repository becomes public. Always provide a template file.env.examplethat lists all required variables with placeholder values:

.env.example

PORT=3000

NODE_ENV=development

DB_HOST=localhost

DB_PORT=5432

DB_NAME=your_database

DB_USER=your_username

DB_PASSWORD=your_password

JWT_SECRET=your_jwt_secret_here

API_KEY=your_api_key_here

Other developers can copy .env.example to .env and fill in their own values. This keeps the project setup clear and consistent.

Use Different Files for Different Environments

As mentioned earlier, avoid using a single .env file for all environments. Use .env.development, .env.production, etc., to isolate configuration. This reduces the risk of accidentally using development credentials in production.

Do Not Store Secrets in Code or Logs

Even with dotenv, never log environment variables or include them in error messages. For example, avoid this:

console.log('Connecting to DB:', process.env.DB_PASSWORD); // ? Dangerous!

Instead, log only the connection status:

console.log('Connecting to database at', process.env.DB_HOST); // ? Safe

Use Environment Variables for All Configuration

Dont just use dotenv for passwords. Use it for everything that changes between environments: API endpoints, cache timeouts, feature flags, logging levels, and third-party service URLs. This makes your application truly portable.

Keep Variables Organized and Documented

As your project grows, your .env file can become unwieldy. Group related variables logically and document their purpose in .env.example:

Database Configuration

DB_HOST=localhost

DB_PORT=5432

DB_NAME=myapp

DB_USER=admin

DB_PASSWORD=secret

Authentication

JWT_SECRET=your_32_char_secret_here

JWT_EXPIRES_IN=24h

External APIs

STRIPE_SECRET_KEY=sk_test_...

TWILIO_ACCOUNT_SID=AC...

TWILIO_AUTH_TOKEN=your_auth_token

This improves onboarding and reduces confusion among team members.

Use a Configuration Module for Complex Apps

For large applications, consider creating a dedicated configuration module to encapsulate dotenv logic and provide type safety (if using TypeScript) or validation:

// config/index.js

require('dotenv').config();

const config = {

port: process.env.PORT || 3000,

env: process.env.NODE_ENV || 'development',

db: {

host: process.env.DB_HOST,

port: parseInt(process.env.DB_PORT, 10) || 5432,

name: process.env.DB_NAME,

user: process.env.DB_USER,

password: process.env.DB_PASSWORD,

},

jwt: {

secret: process.env.JWT_SECRET,

expiresIn: process.env.JWT_EXPIRES_IN || '1d',

},

};

module.exports = config;

Then import it in your app:

const config = require('./config');

app.listen(config.port, () => {

console.log(Server running on port ${config.port});

});

This approach keeps your code DRY, testable, and scalable.

Use Docker and Environment Variables Together

If youre deploying your Node.js app with Docker, you can pass environment variables at runtime using the -e flag or a .env file with docker-compose:

docker-compose.yml

version: '3.8'

services:

app:

build: .

ports:

- "3000:3000"

env_file:

- .env.production

environment:

- NODE_ENV=production

This ensures your containerized app uses the correct configuration without hardcoding values into the image.

Rotate Secrets Regularly

Even with dotenv, secrets stored in files are vulnerable if the file system is compromised. Implement a policy to rotate API keys, database passwords, and JWT secrets regularlyespecially after team member changes or security incidents.

Tools and Resources

Dotenv-Parser

For advanced parsing needs (like handling arrays or nested objects), consider dotenv-parse or dotenv-expand. These tools extend dotenvs functionality:

  • dotenv-expand: Allows variable expansion within .env files. Example: DB_URL=postgres://${DB_USER}:${DB_PASSWORD}@${DB_HOST}
  • dotenv-safe: Validates required variables and provides default values. Useful for stricter environments.

Install dotenv-expand:

npm install dotenv-expand

Usage:

require('dotenv').config();

require('dotenv-expand')(process.env);

VS Code Extensions

Enhance your .env editing experience with these extensions:

  • DotENV Syntax highlighting and autocomplete for .env files
  • ENV Color-codes keys and values, provides quick editing

Online .env Validators

Before committing a .env.example file, validate its syntax using online tools like:

These tools check for invalid characters, missing equals signs, or malformed entries that could cause silent failures.

Security Scanning Tools

Use tools like TruffleHog or GitGuardian to scan your repositories for accidentally committed secretseven if you think youve ignored .env. These tools detect patterns matching API keys, tokens, and passwords in commits.

Environment Variable Managers

For teams managing many projects, consider centralized secret management tools:

  • Vault by HashiCorp Enterprise-grade secrets management
  • AWS Secrets Manager For apps hosted on AWS
  • 1Password or Bitwarden For storing and sharing .env templates securely

These are especially valuable for production deployments where environment variables should not be stored in files at all.

Documentation Resources

Real Examples

Example 1: Express.js API with MongoDB

Lets build a minimal Express API that connects to MongoDB using dotenv.

.env

MONGO_URI=mongodb://localhost:27017/myapi

PORT=5000

NODE_ENV=development

JWT_SECRET=your_jwt_secret_here

server.js

const express = require('express');

const mongoose = require('mongoose');

require('dotenv').config();

const app = express();

app.use(express.json());

// Connect to MongoDB

mongoose.connect(process.env.MONGO_URI)

.then(() => console.log('MongoDB connected'))

.catch(err => console.error('MongoDB connection error:', err));

// Simple route

app.get('/api/users', (req, res) => {

res.json({ message: 'Hello from API!' });

});

const PORT = process.env.PORT || 5000;

app.listen(PORT, () => {

console.log(Server running on port ${PORT});

});

package.json

{

"name": "express-mongo-api",

"version": "1.0.0",

"main": "server.js",

"scripts": {

"start": "node server.js",

"dev": "nodemon server.js"

},

"dependencies": {

"express": "^4.18.2",

"mongoose": "^7.6.0",

"dotenv": "^16.4.5"

},

"devDependencies": {

"nodemon": "^3.0.2"

}

}

This setup allows developers to run the app locally without modifying code. Production deployments can override MONGO_URI via environment variables in the hosting platform.

Example 2: Node.js CLI Tool with API Keys

Imagine youre building a CLI tool that interacts with a third-party service like GitHub:

.env

GITHUB_TOKEN=ghp_abc123xyz789

API_BASE_URL=https://api.github.com

cli.js

!/usr/bin/env node

const { request } = require('http');

const require('dotenv').config();

const token = process.env.GITHUB_TOKEN;

const baseUrl = process.env.API_BASE_URL;

if (!token) {

console.error('Error: GITHUB_TOKEN is not set in .env file.');

process.exit(1);

}

const options = {

hostname: 'api.github.com',

path: '/user',

headers: {

'Authorization': Bearer ${token},

'User-Agent': 'my-cli-tool'

}

};

const req = request(options, res => {

let data = '';

res.on('data', chunk => data += chunk);

res.on('end', () => {

console.log(JSON.parse(data).login);

});

});

req.on('error', err => {

console.error('Request failed:', err.message);

});

req.end();

Run with:

node cli.js

This ensures the API key is never hardcoded into the script and can be changed per user without touching the source code.

Example 3: Testing with Jest and Environment Variables

When writing unit tests, you often need to mock environment variables. Dotenv makes this easy:

.env.test

NODE_ENV=test

DB_HOST=localhost

DB_NAME=test_db

test/database.test.js

const { Pool } = require('pg');

require('dotenv').config({ path: '.env.test' });

describe('Database Connection', () => {

test('should connect to test database', async () => {

const pool = new Pool({

host: process.env.DB_HOST,

database: process.env.DB_NAME,

});

const res = await pool.query('SELECT 1');

expect(res.rows[0].?column?).toBe(1);

});

});

Run tests with:

npm run test

Since the test environment uses a dedicated .env file, you avoid polluting your development database during testing.

FAQs

What happens if I dont use dotenv?

If you dont use dotenv, youll likely hardcode configuration values into your source code. This exposes secrets in version control, makes deployments inconsistent across environments, and increases the risk of accidental leaks. It also makes collaboration harderevery developer must manually edit config files, leading to merge conflicts and errors.

Can I use dotenv in production?

Yesbut with caution. While dotenv works in production, its generally recommended to set environment variables directly in your hosting platform (e.g., Heroku, Vercel, AWS, Docker) rather than using a .env file. This avoids storing secrets on the filesystem. Dotenv can still be used locally to simulate production variables during testing.

Is dotenv secure?

Dotenv itself is secureit simply reads a file and assigns values to process.env. However, security depends on how you manage the .env file. Never commit it to Git. Restrict file permissions. Use encrypted secret managers for production. Dotenv is a tool; security is your responsibility.

Can I use dotenv with TypeScript?

Yes. Install the type definitions:

npm install --save-dev @types/dotenv

Then use it the same way:

import dotenv from 'dotenv';

dotenv.config();

console.log(process.env.PORT);

TypeScript will now recognize process.env properties. For better type safety, define an interface:

interface Env {

PORT: string;

NODE_ENV: string;

DB_HOST: string;

}

declare global {

namespace NodeJS {

interface ProcessEnv extends Env {}

}

}

Why does my .env file not load?

Common causes:

  • Missing require('dotenv').config(); at the top of your file.
  • File is named incorrectly (e.g., env instead of .env).
  • File is in the wrong directoryit must be in the root of your project.
  • Youre using ES6 modules but forgot to use import syntax.
  • Another module (like a config loader) runs before dotenv and tries to access process.env too early.

Do I need to restart my server after changing .env?

Yes. Environment variables are loaded only once when the application starts. Changes to .env files require a server restart to take effect. Tools like nodemon can auto-restart the server on file changes, making development smoother.

Can I use dotenv with React or other frontend frameworks?

Nodotenv is a Node.js module and does not work in browsers. Frontend frameworks like React use different methods (e.g., Vites VITE_ prefix or Create React Apps REACT_APP_ prefix) to expose environment variables. Never use dotenv in frontend code to store secretsthose would be exposed to users.

How do I handle arrays or complex objects in .env?

Dotenv doesnt natively support arrays or nested objects. Store them as comma-separated strings:

ALLOWED_ORIGINS=http://localhost:3000,https://example.com

Then parse in code:

const allowedOrigins = process.env.ALLOWED_ORIGINS.split(',');

For complex objects, use JSON strings:

API_CONFIG={"baseUrl":"https://api.example.com","timeout":5000}

Parse with:

const apiConfig = JSON.parse(process.env.API_CONFIG);

Conclusion

Dotenv is a simple yet indispensable tool for any Node.js developer serious about clean, secure, and maintainable code. By externalizing configuration into environment variables, you decouple your application from its deployment environment, reduce security risks, and improve collaboration across teams.

This guide has walked you through the full lifecycle of using dotenvfrom installation and basic usage to advanced patterns like multi-environment files, validation, and integration with testing and deployment pipelines. Youve seen real-world examples in Express.js, CLI tools, and testing frameworks, and learned best practices that align with industry standards like the Twelve-Factor App methodology.

Remember: dotenv is not a magic solution. Its a foundation. The real power comes from how you use it. Never commit .env files. Validate critical variables. Use separate files for each environment. Combine it with secure secret management in production. And always document your configuration clearly.

As your Node.js applications grow in complexity, mastering dotenv will save you countless hours of debugging, reduce deployment failures, and make your codebase more professional and trustworthy. Start using it todayand never hardcode another password again.