Environment Variables Explained
Managing Configuration & Secrets the Right Way

Config should be strictly separated from code. This is Principle III of the "12-Factor App" methodology. Why? Because you might commit your code to GitHub, but you definitely don't want to commit your AWS Secret Keys or Database Passwords.

Table of Contents

What are Environment Variables?

They are key-value pairs that exist outside your application, provided by the Operating System or the Container (Docker) running your app.

# Examples of Env Vars PORT=3000 HOST=localhost NODE_ENV=development DATABASE_URL=postgres://user:pass@localhost:5432/mydb API_KEY=sk_live_123456789

Why Use Them?

1. Security

Never hardcode secrets. If you put const apiKey = "123" in your code and push it to a public repo, bots will find it in seconds.

2. Flexibility (Write Once, Run Anywhere)

The code shouldn't change between environments. Only the config should.

The code remains: connect(process.env.DATABASE_URL)

The .env File Standard

In development, setting system-wide variables is annoying. Enter the dotenv pattern. You create a file named .env in your project root:

# .env file content PORT=8080 DB_HOST=localhost DB_USER=admin DB_PASS=secretPassword123
CRITICAL: Add .env to your .gitignore file immediately! Never commit this file.

Instead, commit a file named .env.example that contains the keys but NOT the values:

# .env.example (Safe to commit) PORT=8080 DB_HOST= DB_USER= DB_PASS=

Setting Env Vars by OS

Windows (PowerShell)

# Temporary (current session) $env:PORT = "3000" # Persistent (user-level) setx PORT "3000"

macOS / Linux (bash/zsh)

# Temporary (current shell) export PORT=3000 # Persistent (add to ~/.zshrc or ~/.bashrc) export PORT=3000

Note: Persistent variables require a new terminal session to take effect.

Precedence Rules (Who Wins?)

When multiple sources define the same variable, precedence matters.

Practical tip: In Next.js, .env.local overrides .env, and NEXT_PUBLIC_* variables are exposed to the browser.

Accessing Env Vars in Code

Node.js / JavaScript / Next.js

// 1. Install dotenv: npm install dotenv // 2. Load it early require('dotenv').config(); // 3. Access const port = process.env.PORT || 3000; const dbUrl = process.env.DATABASE_URL; console.log(`Server starting on port ${port}`);

Python

import os from dotenv import load_dotenv # pip install python-dotenv load_dotenv() port = os.getenv("PORT", 3000) # Defaults to 3000 if not set api_key = os.environ.get("API_KEY")

Java (Spring Boot)

Spring Boot loads application.properties which can reference env vars:

server.port=${PORT:8080} spring.datasource.url=${DATABASE_URL}

Environment Variables in CI/CD

Production environments should inject variables via deployment platforms, not files.

Common Pitfalls

Best Practices Summary

  • ✅ Always add .env to .gitignore.
  • ✅ Use UPPER_SNAKE_CASE for variable names.
  • ✅ Provide defaults in your code if the variable is optional.
  • ✅ In Production (AWS, Heroku, Vercel), set these variables in the platform dashboard, not in a file.
  • ✅ Use .env.local, .env.test for framework-specific overrides (like in Next.js).