Tutorials

Environment Variables in Node.js

February 17, 2026
12 min read read
M
Manas

Environment variables are essential for configuring Node.js applications. They keep sensitive data out of your code and allow different configurations for development, staging, and production.

What Are Environment Variables?

Environment variables are key-value pairs available to your application at runtime. They are set outside your code, making it easy to change configuration without modifying source files.

// Accessing environment variables
const port = process.env.PORT;
const dbUrl = process.env.DATABASE_URL;

Why Use Environment Variables?

Security

Keep sensitive data out of your codebase:

  • API keys
  • Database credentials
  • Secret tokens
  • Third-party service credentials

Flexibility

Different values for different environments:

  • Development database vs production database
  • Debug mode in development
  • Different API endpoints

Portability

Same code works in different environments without changes.

Setting Environment Variables

Method 1: Command Line

# Single variable
PORT=3000 node app.js

# Multiple variables
PORT=3000 NODE_ENV=production node app.js

Method 2: Export (Unix/Mac)

export PORT=3000
export NODE_ENV=production
node app.js

Method 3: .env File (Recommended)

Create a .env file:

PORT=3000
NODE_ENV=development
DATABASE_URL=mongodb://localhost:27017/myapp
API_KEY=your_secret_api_key

Using dotenv

dotenv loads variables from .env files into process.env.

Installation

npm install dotenv

Basic Usage

// At the very top of your entry file
require('dotenv').config();

// Now you can use process.env
console.log(process.env.PORT);
console.log(process.env.DATABASE_URL);

ES Modules

import 'dotenv/config';
// or
import dotenv from 'dotenv';
dotenv.config();

Custom Path

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

Environment-Specific Files

Multiple .env Files

.env                # Default/shared variables
.env.development    # Development-specific
.env.production     # Production-specific
.env.test           # Test-specific
.env.local          # Local overrides (gitignored)

Loading Based on Environment

const envFile = process.env.NODE_ENV === 'production' 
  ? '.env.production' 
  : '.env.development';

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

Using dotenv-flow

For automatic environment file loading:

npm install dotenv-flow
require('dotenv-flow').config();

This automatically loads:

  1. .env
  2. .env.local
  3. .env.[NODE_ENV]
  4. .env.[NODE_ENV].local

Configuration Module Pattern

Create a centralized config:

config.js

require('dotenv').config();

module.exports = {
  port: process.env.PORT || 3000,
  nodeEnv: process.env.NODE_ENV || 'development',
  
  database: {
    url: process.env.DATABASE_URL,
    name: process.env.DB_NAME || 'myapp'
  },
  
  jwt: {
    secret: process.env.JWT_SECRET,
    expiresIn: process.env.JWT_EXPIRES_IN || '7d'
  },
  
  discord: {
    token: process.env.DISCORD_TOKEN,
    clientId: process.env.DISCORD_CLIENT_ID
  },
  
  isDevelopment: process.env.NODE_ENV === 'development',
  isProduction: process.env.NODE_ENV === 'production'
};

Usage

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

app.listen(config.port, () => {
  console.log(`Server running on port ${config.port}`);
});

if (config.isDevelopment) {
  // Development-only code
}

Validation

Validate required variables at startup:

Simple Validation

const required = ['DATABASE_URL', 'JWT_SECRET', 'DISCORD_TOKEN'];

for (const key of required) {
  if (!process.env[key]) {
    console.error(`Missing required environment variable: ${key}`);
    process.exit(1);
  }
}

Using envalid

npm install envalid
const { cleanEnv, str, port, url } = require('envalid');

const env = cleanEnv(process.env, {
  NODE_ENV: str({ choices: ['development', 'production', 'test'] }),
  PORT: port({ default: 3000 }),
  DATABASE_URL: url(),
  JWT_SECRET: str(),
  DISCORD_TOKEN: str()
});

// env is now validated and typed
console.log(env.PORT); // number
console.log(env.isDev); // boolean helper

Security Best Practices

1. Never Commit .env Files

Add to .gitignore:

.env
.env.local
.env.*.local

2. Provide Example File

Create .env.example with placeholder values:

PORT=3000
DATABASE_URL=mongodb://localhost:27017/myapp
JWT_SECRET=your_jwt_secret_here
DISCORD_TOKEN=your_discord_token_here

3. Use Different Values Per Environment

Never use production credentials in development.

4. Rotate Secrets Regularly

Change API keys and tokens periodically.

5. Limit Access

Only give team members the credentials they need.

Common Patterns

Discord Bot

# .env
DISCORD_TOKEN=your_bot_token
DISCORD_CLIENT_ID=your_client_id
DISCORD_GUILD_ID=your_guild_id
PREFIX=!
const { Client, GatewayIntentBits } = require('discord.js');
require('dotenv').config();

const client = new Client({
  intents: [GatewayIntentBits.Guilds]
});

client.login(process.env.DISCORD_TOKEN);

Express API

# .env
PORT=3000
NODE_ENV=development
DATABASE_URL=mongodb://localhost:27017/api
JWT_SECRET=super_secret_key
CORS_ORIGIN=http://localhost:3001
require('dotenv').config();
const express = require('express');
const cors = require('cors');

const app = express();

app.use(cors({
  origin: process.env.CORS_ORIGIN
}));

app.listen(process.env.PORT);

Database Connection

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

mongoose.connect(process.env.DATABASE_URL, {
  useNewUrlParser: true,
  useUnifiedTopology: true
});

Hosting Provider Configuration

HeavenCloud

Set environment variables in the control panel or use .env files.

PM2

// ecosystem.config.js
module.exports = {
  apps: [{
    name: 'my-app',
    script: 'app.js',
    env: {
      NODE_ENV: 'development'
    },
    env_production: {
      NODE_ENV: 'production',
      PORT: 8080
    }
  }]
};

Docker

# Dockerfile
ENV NODE_ENV=production
ENV PORT=3000

Or with docker-compose:

services:
  app:
    build: .
    environment:
      - NODE_ENV=production
      - PORT=3000
    env_file:
      - .env.production

Troubleshooting

Variables Not Loading

  1. Ensure dotenv is loaded first
  2. Check file path is correct
  3. Verify .env file syntax (no spaces around =)

Undefined Values

// Check if variable exists
if (!process.env.MY_VAR) {
  console.log('MY_VAR is not set');
}

// Provide defaults
const myVar = process.env.MY_VAR || 'default_value';

Type Issues

Environment variables are always strings:

// Wrong
if (process.env.ENABLE_FEATURE) // Always truthy if set

// Correct
if (process.env.ENABLE_FEATURE === 'true')

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

Conclusion

Environment variables are fundamental to Node.js application configuration. Use dotenv for local development, validate required variables at startup, and never commit sensitive data to version control.

HeavenCloud makes environment variable management easy with built-in support in the control panel for all hosting plans.

Start building your community

Deploy high-performance Discord bots, Lavalink nodes, and VPS servers with instant setup on HeavenCloud.