How to Use Dotenv in Nodejs

How to Use Dotenv in Node.js Managing configuration settings in Node.js applications can quickly become chaotic as projects grow. Hardcoding API keys, database credentials, and environment-specific variables directly into your source code is not only insecure—it’s a violation of modern software development best practices. This is where Dotenv comes in. Dotenv is a zero-dependency module that loads

Nov 6, 2025 - 11:18
Nov 6, 2025 - 11:18
 6

How to Use Dotenv in Node.js

Managing configuration settings in Node.js applications can quickly become chaotic as projects grow. Hardcoding API keys, database credentials, and environment-specific variables directly into your source code is not only insecureits a violation of modern software development best practices. This is where Dotenv comes in. Dotenv is a zero-dependency module that loads environment variables from a .env file into process.env, making your Node.js applications more secure, portable, and maintainable.

In this comprehensive guide, youll learn exactly how to use Dotenv in Node.jsfrom initial setup to advanced configurations and real-world implementations. Whether youre building a REST API, a microservice, or a full-stack application, mastering Dotenv is essential for professional-grade development. By the end of this tutorial, youll understand not just how to install and use Dotenv, but how to integrate it into your workflow with confidence and precision.

Step-by-Step Guide

1. Prerequisites

Before diving into Dotenv, ensure you have the following installed:

  • Node.js (v14 or higher recommended)
  • npm or yarn (package managers)
  • A code editor (VS Code, Sublime, or similar)
  • Basic familiarity with JavaScript and Node.js modules

You can verify your Node.js and npm versions by running the following commands in your terminal:

node -v

npm -v

If you dont have Node.js installed, visit nodejs.org to download the latest LTS version.

2. Initialize a Node.js Project

If youre starting from scratch, create a new directory and initialize a Node.js project:

mkdir my-node-app

cd my-node-app

npm init -y

This creates a package.json file with default settings. You can later customize it with scripts, dependencies, and metadata as needed.

3. Install Dotenv

Install Dotenv as a dependency using npm:

npm install dotenv

Alternatively, if youre using yarn:

yarn add dotenv

Once installed, Dotenv will appear in your package.json under the dependencies section:

"dependencies": {

"dotenv": "^16.4.5"

}

4. Create a .env File

In the root directory of your project, create a new file named .env. This file will store your environment variables in a simple key-value format:

DB_HOST=localhost

DB_PORT=5432

DB_NAME=myapp_db

DB_USER=admin

DB_PASS=securepassword123

API_KEY=your_secret_api_key_here

NODE_ENV=development

PORT=3000

Important notes about the .env file:

  • Do not include spaces around the = sign.
  • Values with spaces or special characters should be wrapped in double quotes: SECRET="my secret value with spaces"
  • Comments are not supported in .env files. Avoid using

    for notes.
  • Never commit this file to version control (see Best Practices below).

5. Load Environment Variables in Your Application

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

Create index.js in your project root and add the following:

require('dotenv').config();

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

console.log(process.env.API_KEY); // Output: your_secret_api_key_here

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

The require('dotenv').config(); line reads the .env file and populates process.env with the variables defined inside. Once loaded, you can access them anywhere in your application using process.env.VARIABLE_NAME.

6. Use Environment Variables in Your Code

Now that variables are loaded, integrate them into your application logic. Heres a practical example using Express.js:

require('dotenv').config();

const express = require('express');

const app = express();

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

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

res.send(Server running on port ${port}. Environment: ${process.env.NODE_ENV});

});

app.listen(port, () => {

console.log(App is running at http://localhost:${port});

});

Notice how we provide a fallback value (|| 5000) in case PORT is not defined in the .env file. This is a common pattern to ensure your app doesnt crash in development environments where the .env file might be missing.

7. Configure Dotenv with Custom Options

Dotenv offers several configuration options to customize its behavior. The config() method accepts an object with optional parameters:

  • path: Specify a custom path to your .env file (default: ./.env)
  • encoding: Set file encoding (default: utf8)
  • debug: Enable debugging output (useful for troubleshooting)
  • override: If true, existing environment variables will be overwritten by .env values

Example with custom options:

require('dotenv').config({

path: './config/.env',

encoding: 'utf8',

debug: process.env.NODE_ENV === 'development',

override: true

});

In this example:

  • The .env file is located in a config/ subdirectory
  • Debug mode is enabled only in development
  • Existing system environment variables are overwritten if the same key exists in .env

8. Using Dotenv with TypeScript

If youre using TypeScript, youll need to declare types for your environment variables to avoid TypeScript errors. Create a file named env.d.ts in your project root:

declare namespace NodeJS {

interface ProcessEnv {

NODE_ENV: 'development' | 'production' | 'test';

PORT: string;

DB_HOST: string;

DB_PORT: string;

DB_NAME: string;

DB_USER: string;

DB_PASS: string;

API_KEY: string;

}

}

This tells TypeScript what environment variables to expect, enabling autocompletion and type safety. You can then safely use process.env.PORT without TypeScript complaining about missing properties.

9. Testing Your Setup

To verify everything is working, add a simple script to your package.json:

"scripts": {

"start": "node index.js",

"dev": "nodemon index.js"

}

Install nodemon for auto-reloading during development:

npm install -D nodemon

Then run:

npm run dev

You should see output like:

App is running at http://localhost:3000

Visit http://localhost:3000 in your browser to confirm the server is live.

10. Handling Missing Environment Variables

Its good practice to validate required environment variables at startup. Add a validation function to your index.js:

require('dotenv').config();

const requiredEnvVars = ['DB_HOST', 'DB_PORT', 'DB_NAME', 'API_KEY', 'NODE_ENV'];

requiredEnvVars.forEach(varName => {

if (!process.env[varName]) {

throw new Error(Missing required environment variable: ${varName});

}

});

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

This prevents your app from starting with incomplete configuration, which could lead to silent failures or security issues.

Best Practices

1. Never Commit .env to Version Control

The .env file contains sensitive data such as passwords, API keys, and database credentials. Never commit it to Git or any public repository. Add it to your .gitignore file:

.env

.env.local

.env.*.local

Instead, create a template file named .env.example that includes all required keys with placeholder values:

.env.example

DB_HOST=localhost

DB_PORT=5432

DB_NAME=your_database_name

DB_USER=your_username

DB_PASS=your_password

API_KEY=your_api_key_here

NODE_ENV=development

PORT=3000

Commit .env.example to your repository so other developers know which variables are needed. They can then copy it to .env and fill in their own values.

2. Use Different .env Files for Different Environments

For production, staging, and development environments, use separate .env files:

  • .env.development for local development
  • .env.staging for staging servers
  • .env.production for production deployment

Then load the correct file based on the NODE_ENV variable:

const env = process.env.NODE_ENV || 'development';

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

This allows you to use different database connections, API endpoints, or logging levels per environment without changing code.

3. Use a .env.local for Personal Overrides

Some developers prefer to have a local override file thats never shared. Add .env.local to your .gitignore and load it conditionally:

const env = process.env.NODE_ENV || 'development';

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

require('dotenv').config({ path: '.env.local', override: true });

This lets you override specific values locally (e.g., a different database port) without affecting the teams shared configuration.

4. Avoid Storing Secrets in Code

Never hardcode secrets in your source codeeven in comments or test files. Even if you think the code is private, leaks happen. Always use environment variables.

5. Validate and Sanitize Inputs

Environment variables are strings by default. Always validate and convert them to the correct type:

const port = parseInt(process.env.PORT, 10);

if (isNaN(port) || port 65535) {

throw new Error('PORT must be a valid port number between 1 and 65535');

}

Similarly, convert boolean values:

const isDebug = process.env.DEBUG === 'true';

6. Use Docker and CI/CD with Dotenv

When deploying to Docker or CI/CD pipelines (like GitHub Actions, Jenkins, or CircleCI), pass environment variables directly via the container or pipeline configuration instead of using a .env file. This keeps secrets out of your repository entirely.

Example Docker Compose:

services:

app:

build: .

environment:

- NODE_ENV=production

- DB_HOST=db

- API_KEY=${API_KEY}

ports:

- "3000:3000"

Here, ${API_KEY} is pulled from your host machines environment, not from a file.

7. Restrict File Permissions

On Unix-based systems, ensure your .env file has restricted permissions:

chmod 600 .env

This ensures only the owner can read or write to the file, reducing the risk of accidental exposure.

8. Use Dotenv-Extended for Advanced Use Cases

If you need more advanced features like variable interpolation or nested objects, consider dotenv-extended:

npm install dotenv-extended

Then use it like:

require('dotenv-extended').load();

It supports features like:

  • Variable interpolation: DB_URL=postgres://${DB_USER}:${DB_PASS}@${DB_HOST}:${DB_PORT}/${DB_NAME}
  • Default values: PORT=${PORT:-3000}
  • Multiple file loading

However, for most use cases, the original Dotenv is sufficient and lighter.

Tools and Resources

1. VS Code Extensions

Several VS Code extensions improve the .env file experience:

  • .env Syntax highlighting and autocomplete for .env files
  • DotENV Provides IntelliSense for environment variables
  • Environment Variables Quick view and edit of .env variables

Install any of these from the VS Code Marketplace to improve productivity and reduce typos.

2. Online .env Validators

Before deploying, validate your .env syntax using online tools:

These are especially helpful when collaborating with teams or automating deployment pipelines.

3. Secret Management Alternatives

While Dotenv is excellent for local development and small to medium projects, consider these tools for enterprise applications:

  • AWS Secrets Manager For applications hosted on AWS
  • Vault by HashiCorp Centralized secrets management with dynamic secrets
  • 1Password Secrets Automation Integrates with CI/CD and developer workflows
  • Google Secret Manager For GCP-hosted applications

These tools offer encryption, audit logs, rotation, and access controlfeatures not available in Dotenv. Use them when security and compliance are critical.

4. Documentation and Learning Resources

Official documentation and tutorials:

5. Automated Testing Tools

When writing unit tests, use dotenv to load test-specific variables:

// __tests__/config.test.js

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

test('PORT is set', () => {

expect(process.env.PORT).toBeDefined();

});

Use libraries like jest-environment-node or supertest to simulate environment-specific behavior in tests.

Real Examples

Example 1: Express.js API with MongoDB

Lets build a simple Express API that connects to MongoDB using Dotenv.

Install required packages:

npm install express mongoose dotenv

Create .env:

MONGO_URI=mongodb://localhost:27017/myapi

NODE_ENV=development

PORT=5000

JWT_SECRET=my_super_secret_jwt_key

Create server.js:

require('dotenv').config();

const express = require('express');

const mongoose = require('mongoose');

const app = express();

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

// 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('/', (req, res) => {

res.json({

message: 'Hello from Node.js API',

environment: process.env.NODE_ENV,

port: port

});

});

app.listen(port, () => {

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

});

Run with node server.js. The app connects to MongoDB using the URI from .env and returns a JSON response.

Example 2: Email Service with SendGrid

Send emails using SendGrids API with Dotenv for secure key storage.

Install SendGrid:

npm install @sendgrid/mail

Add to .env:

SENDGRID_API_KEY=SG.your_api_key_here

SENDER_EMAIL=noreply@yourdomain.com

Create emailService.js:

require('dotenv').config();

const sgMail = require('@sendgrid/mail');

sgMail.setApiKey(process.env.SENDGRID_API_KEY);

const sendWelcomeEmail = async (email, name) => {

const msg = {

to: email,

from: process.env.SENDER_EMAIL,

subject: 'Welcome to Our App!',

text: Hello ${name}, welcome aboard!, html: Hello ${name}, welcome aboard!,

};

try {

await sgMail.send(msg);

console.log('Email sent successfully');

} catch (error) {

console.error('Error sending email:', error);

}

};

module.exports = { sendWelcomeEmail };

Import and use in your Express route:

const { sendWelcomeEmail } = require('./emailService');

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

const { email, name } = req.body;

sendWelcomeEmail(email, name);

res.json({ message: 'User registered and email sent' });

});

Example 3: Dockerized Node.js App

Create a Dockerfile:

FROM node:18-alpine

WORKDIR /app

COPY package*.json ./

RUN npm install --production

COPY . .

Do NOT copy .env into the image for security

Pass it at runtime instead

EXPOSE 3000

CMD ["node", "server.js"]

Create a docker-compose.yml:

version: '3.8'

services:

app:

build: .

ports:

- "3000:3000"

environment:

- NODE_ENV=production

- MONGO_URI=mongodb://mongo:27017/myapp

- SENDGRID_API_KEY=${SENDGRID_API_KEY}

depends_on:

- mongo

mongo:

image: mongo:5

ports:

- "27017:27017"

volumes:

- mongo_data:/data/db

volumes:

mongo_data:

Run with:

SENDGRID_API_KEY=your_key_here docker-compose up

This approach keeps secrets out of the Docker image entirely, relying on host environment variables at runtime.

FAQs

Can I use Dotenv in browser applications?

No. Dotenv is designed for Node.js server-side applications. Browser environments cannot access the file system or environment variables in the same way. If you need configuration in the frontend, use build-time variables (e.g., Vite, Webpack DefinePlugin) and avoid exposing secrets.

What happens if I dont load Dotenv at the top of my file?

If you load Dotenv after importing modules that rely on environment variables, those modules may fail or use default values. Always call require('dotenv').config(); as the first line in your main entry file.

Is Dotenv secure for production?

Dotenv is safe for development and small-scale production use. For enterprise applications, consider using dedicated secret managers (like AWS Secrets Manager or HashiCorp Vault) to manage secrets with encryption, rotation, and access controls.

Can I use Dotenv with multiple .env files?

Yes. You can load multiple files by calling config() multiple times, but be cautious of overwrites. Use override: true only when you intend to replace existing values.

Why are my environment variables undefined?

Common causes:

  • Dotenv isnt loaded before the variable is accessed
  • The .env file is in the wrong directory
  • Typo in the variable name (case-sensitive)
  • File encoding issues (e.g., UTF-16 instead of UTF-8)
  • Running the app from a different directory than the .env file

Enable debug mode: require('dotenv').config({ debug: true }); to see whats being loaded.

How do I reset environment variables between tests?

Use jest.resetModules() or manually delete variables after each test:

beforeEach(() => {

delete process.env.API_KEY;

});

Or use a library like dotenv-flow or mock-environment for better test isolation.

Does Dotenv support nested objects?

No. Dotenv only supports flat key-value pairs. For nested structures, use JSON strings and parse them:

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

Then in code:

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

Can I use Dotenv with Next.js?

Yes, but Next.js has its own built-in environment variable system. Use .env.local and prefix variables with NEXT_PUBLIC_ for client-side access. Dotenv is not required in Next.js projects.

Whats the difference between process.env and Dotenv?

process.env is a built-in Node.js object that holds environment variables from the system. Dotenv is a library that reads a .env file and populates process.env with those values. Dotenv is a tool to make process.env easier to manage.

Conclusion

Dotenv is an indispensable tool for any Node.js developer serious about writing clean, secure, and maintainable applications. By separating configuration from code, you reduce the risk of credential leaks, simplify deployment across environments, and make your codebase more adaptable.

In this guide, youve learned how to install and configure Dotenv, structure your .env files, handle edge cases, and integrate it into real-world applicationsfrom Express APIs to Docker deployments. Youve also explored best practices that ensure your secrets remain secure and your configurations remain consistent across teams and environments.

Remember: Dotenv is not a replacement for enterprise-grade secret management systems, but its the perfect starting point for developers building modern Node.js applications. As your project scales, you can evolve your approachperhaps integrating with Vault or cloud secrets managersbut for now, mastering Dotenv is a foundational step toward professional development.

Start using Dotenv today. Create your .env file. Load your variables. Secure your secrets. And build better softwareconfidently.