NIXX/DEVv1.14.0
ArticlesFavorites
Sign In
Sign In
Articles

Welcome to our blog

A curated collection of insightful articles, practical guides, and expert tips designed to simplify your workflow

Cover image for: How to Use Environment Variables Securely in Node.js
September 25, 20255 MIN READ min readBy ℵi✗✗

How to Use Environment Variables Securely in Node.js

Stop hardcoding secrets in your Node.js apps. This guide shows you how to use environment variables securely with .env files, dotenv, and secret managers—covering local setup, production best practices, and common pitfalls to avoid.

webdevNode.js
ℵi✗✗

ℵi✗✗

Full-Stack Developer

Passionate about building tools and sharing knowledge with the developer community.

Was this helpful?

Popular Posts

  • NixOS vs. Arch Linux: Which One Belongs in Your Dev Setup?

    NixOS vs. Arch Linux: Which One Belongs in Your Dev Setup?

    5 MIN READ min read

  • How to Enable HTTPS on Localhost in Under 2 Minutes

    How to Enable HTTPS on Localhost in Under 2 Minutes

    3 MIN READ min read

  • Migrating from Create React App (CRA) to Vite: A Step-by-Step Guide

    Migrating from Create React App (CRA) to Vite: A Step-by-Step Guide

    4 MIN READ min read

  • Array Destructuring in PHP: A Practical Guide for Modern Developers

    Array Destructuring in PHP: A Practical Guide for Modern Developers

    5 MIN READ min read

Recommended Products

  • Apple iPad (7th Gen)

    Apple iPad (7th Gen)

    4.3
  • Fitbit Versa 4

    Fitbit Versa 4

    4.3
  • JBL Flip 6

    JBL Flip 6

    4.8
  • Dell 24 Monitor — SE2425HM Full HD

    Dell 24 Monitor — SE2425HM Full HD

    4.7

May contain affiliate links

Topics

webdev33productivity16cybersecurity12javascript11automation9guide8react7typescript7php6tutorial6freelancing5github actions5privacy5how to4Node.js4
+111 more topics →
🇺🇸USD ACCOUNTOpen a free US-based USD accountReceive & save in USD — powered by ClevaSponsoredInterserver Hosting#1 VALUEAffordable, reliable hosting from $2.50/mo99.9% uptimeSponsored

Hardcoded credentials in source code are one of the most common causes of accidental credential exposure. A database password in config.js, an API key in a utility file, a token checked into a commit — any of these can reach a public repository and be scraped by automated scanners within minutes of being pushed.

Environment variables are the standard solution. They keep sensitive values out of the codebase, make it straightforward to maintain different configurations for development, staging, and production, and are natively supported by every deployment platform and container runtime.

This guide covers the complete setup: installing and configuring dotenv, organizing variables across environments, validating required variables at startup, and the practices that matter most for production deployments.

What this covers:

  • Installing dotenv and loading a .env file

  • Accessing environment variables in application code

  • Organizing variables across multiple environments

  • Validating required variables at startup

  • Securing .env files and using secret managers in production

  • Common issues and how to resolve them


Step 1: Install dotenv and Create a .env File

dotenv reads a .env file in the project root and loads its values into process.env when the application starts.

Install it:

npm install dotenv

Create a .env file in the project root:

PORT=4000
DB_HOST=localhost
DB_USER=admin
DB_PASS=supersecretpassword
API_KEY=abcd1234

Add .env to .gitignore immediately. This file should never be committed:

.env
.env.*
!.env.example

The !.env.example exception commits a template file (with placeholder values, no real secrets) so other developers know which variables are required. This is a common and useful convention.


Step 2: Load Environment Variables in the Application

Call require('dotenv').config() at the top of the application entry point, before any other code that reads from process.env:

require('dotenv').config();

const express = require('express');
const app = express();

const PORT = process.env.PORT || 3000;
const DB_HOST = process.env.DB_HOST;
const DB_USER = process.env.DB_USER;

app.get('/', (req, res) => {
    res.send(`App running on port ${PORT}`);
});

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

In an ES module project using import syntax:

import 'dotenv/config';
import express from 'express';

dotenv only reads the .env file. In production environments where variables are injected by the platform (Heroku config vars, Vercel environment settings, Kubernetes secrets), dotenv simply does nothing — it does not override variables that are already set in process.env. This is the correct behavior and means the same code works in both local development and production without modification.


Step 3: Validate Required Variables at Startup

Missing environment variables cause runtime errors at unpredictable points during execution. A database connection that fails because DB_HOST was not set in a new environment is much harder to diagnose than a startup check that reports exactly which variable is missing.

Add validation at the top of the entry file, after loading dotenv but before anything else runs:

require('dotenv').config();

const required = ['DB_HOST', 'DB_USER', 'DB_PASS', 'API_KEY'];

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

Exiting immediately with a clear error message is preferable to letting the application start and fail mysteriously later. In containerized deployments, a clean exit with a descriptive message makes the problem immediately visible in the container logs.

For TypeScript projects or applications with many variables, a schema validation library like zod makes the validation more structured:

const { z } = require('zod');

const envSchema = z.object({
    PORT: z.string().default('3000'),
    DB_HOST: z.string(),
    DB_USER: z.string(),
    DB_PASS: z.string(),
    API_KEY: z.string().min(10),
});

const env = envSchema.parse(process.env);
// env.DB_HOST is now typed as string and validated

Step 4: Organize Variables Across Environments

Different environments typically need different configuration: a local database for development, a staging database for testing, a production database with stricter access controls.

Create separate files for each environment:

.env.development
.env.staging
.env.production

.env.development:

PORT=4000
DB_HOST=localhost
DB_USER=dev_user
DB_PASS=dev_password

.env.production:

PORT=8080
DB_HOST=prod-db.company.com
DB_USER=prod_user
DB_PASS=injected_by_secret_manager

The dotenv-flow package handles loading the correct file based on NODE_ENV automatically:

npm install dotenv-flow
require('dotenv-flow').config();
// Loads .env, then .env.{NODE_ENV}, with later files overriding earlier ones

Run with the appropriate environment:

NODE_ENV=production node server.js
NODE_ENV=development node server.js

Step 5: Use Secret Managers in Production

.env files are suitable for local development. In production, storing secrets in files on the server creates security and operational risks: the file can be read by anyone with server access, it is not audited, and rotating a secret requires manual file editing.

Production environments should use a dedicated secret manager:

  • AWS Secrets Manager — integrates with IAM roles so EC2 instances and Lambda functions can fetch secrets without storing them anywhere

  • Google Secret Manager — similar IAM-based access model for GCP workloads

  • HashiCorp Vault — platform-agnostic, supports dynamic secrets that expire automatically

  • Doppler — developer-friendly interface for managing secrets across environments with team access controls

With a secret manager, the application fetches the secret at startup (or at request time for particularly sensitive values) rather than reading from a file. Most managed deployment platforms (Vercel, Railway, Render, Heroku) also support injecting secrets as environment variables through their dashboards, without requiring a .env file on the server at all.


Common Issues and Fixes

process.env.VARIABLE_NAME is undefined. The most common cause is that require('dotenv').config() is not at the top of the entry file, or the .env file is not in the project root. Check that the file exists, that the variable name matches exactly (environment variables are case-sensitive), and that dotenv is loaded before the variable is accessed.

Variables are not updating after a change. Node.js reads process.env once at startup. After changing a .env file, restart the process. In development, tools like nodemon restart automatically; in production, a process restart or redeployment is required.

Different values on different machines. If a variable is set in the system environment on one machine but not in .env, the values will differ. The .env.example file should document every required variable so developers can create a local .env with the correct names.

Sensitive values appearing in logs. Never log process.env directly — it dumps every environment variable including secrets. Log specific values with placeholders when debugging:

console.log(`DB_HOST: ${process.env.DB_HOST}`);  // acceptable for non-secret config
console.log(`API_KEY: ${process.env.API_KEY ? '[set]' : '[missing]'}`);  // never log the value

Key Takeaways

  • Add .env to .gitignore immediately and commit a .env.example with placeholder values instead.

  • Call require('dotenv').config() or import 'dotenv/config' at the very top of the entry file, before any code that reads process.env.

  • Validate required variables at startup and exit with a clear error message if any are missing. Runtime failures from missing config are harder to diagnose than startup failures.

  • dotenv does not override variables already set in the environment. The same code works in local development and production without modification.

  • Use separate .env.development and .env.production files for environment-specific configuration, managed with dotenv-flow or the deployment platform's built-in secrets management.

  • In production, use a secret manager rather than a .env file on the server.

  • Never log sensitive environment variable values. Log a [set] or [missing] indicator instead.


Conclusion

Environment variables are the established solution for keeping secrets out of source code and making application configuration portable across environments. The setup is lightweight — a .env file, the dotenv package, and a startup validation check — and the security benefit is immediate.

The most important practice is the simplest: add .env to .gitignore before the first commit. Everything else follows from there.


Managing environment variables in a specific deployment setup and running into a problem? Describe the setup in the comments.

Topics
webdevNode.js
Interserver Hosting#1 VALUEAffordable, reliable hosting from $2.50/mo99.9% uptimeSponsored

Discussion

Join the discussion

Sign in to share your thoughts and engage with the community.

Sign In
Loading comments…

Continue Reading

More Articles

View all
Cover image for: Best Web Hosting of 2026 (Honest Picks From Real-World Use)
Jan 1, 20267 MIN READ min read

Best Web Hosting of 2026 (Honest Picks From Real-World Use)

Choosing the right web hosting in 2026 isn't just about price. A breakdown of the best providers, focusing on reliability, performance, and support.

Cover image for: AI for DevOps: Tools That Are Already Changing the Game
Jun 17, 20256 MIN READ min read

AI for DevOps: Tools That Are Already Changing the Game

How artificial intelligence is transforming CI/CD pipelines, monitoring, and incident response—today.

Cover image for: Embedding Cybersecurity in Development: Best Practices for 2025
Jul 1, 20257 MIN READ min read

Embedding Cybersecurity in Development: Best Practices for 2025

A developer-focused guide to integrating security into your workflow—covering tools, practices, and mindset shifts for 2025.

Cover image for: React Authentication with JWT: A Step-by-Step Guide
Oct 17, 20257 MIN READ min read

React Authentication with JWT: A Step-by-Step Guide

Learn how to implement secure JWT authentication in React. From login to route protection and API calls, this guide covers everything you need to know.

|Made with · © 2026|TermsPrivacy
AboutBlogContact

Free, open-source tools for developers and creators · Community driven