Isaac.

Using Environment Variables

Learn how to manage configuration and secrets using environment variables in your applications.

By EMEPublished: February 20, 2025
environment variablesconfigurationsecretssecuritydotnetnode.js

A Simple Analogy

Think of your house with adjustable settings. You don't hardcode "bedroom temperature = 72°F" into your walls—you use a thermostat that you can adjust without rebuilding the house. Environment variables are like that thermostat: they let you change your app's behavior (database URL, API keys, feature flags) without recompiling code.


What Are Environment Variables?

Environment variables are key-value pairs that your application reads at runtime. They're set on the operating system or in deployment platforms (Docker, Kubernetes, cloud services) and allow you to change app behavior without touching code.


Why Use Environment Variables?

  • Security: Keep secrets (API keys, passwords) out of source code
  • Flexibility: Different settings for different environments (dev, staging, production)
  • Easy configuration: Change behavior without redeploying
  • Team safety: Developers don't need production credentials
  • DevOps best practice: Standard way to configure cloud applications

Common Use Cases

  • Database connection strings
  • API keys and tokens
  • Feature flags (enable/disable features)
  • Logging levels
  • Third-party service URLs
  • Port numbers

Setting Environment Variables

Linux/macOS

# Set for current session
export DATABASE_URL="postgresql://localhost/mydb"
export API_KEY="secret123"

# Verify
echo $DATABASE_URL

Windows (PowerShell)

$env:DATABASE_URL = "postgresql://localhost/mydb"
$env:API_KEY = "secret123"

Docker

FROM node:18
WORKDIR /app
COPY . .
ENV NODE_ENV=production
ENV API_KEY=your_secret_key
CMD ["node", "app.js"]

Or with .env file in Docker:

docker run --env-file .env myapp

Reading Environment Variables

Node.js

// Using process.env
const dbUrl = process.env.DATABASE_URL;
const apiKey = process.env.API_KEY;
const port = process.env.PORT || 3000; // Default value

console.log(`Connecting to ${dbUrl}`);
console.log(`Running on port ${port}`);

.NET / C#

// Using IConfiguration
public class Startup
{
    private readonly IConfiguration _config;

    public Startup(IConfiguration config)
    {
        _config = config;
    }

    public void ConfigureServices(IServiceCollection services)
    {
        var dbConnection = _config["ConnectionStrings:DefaultConnection"];
        var apiKey = _config["ApiKey"];
    }
}

// Or directly from environment
var dbUrl = Environment.GetEnvironmentVariable("DATABASE_URL");

Python

import os

db_url = os.getenv("DATABASE_URL")
api_key = os.getenv("API_KEY")
debug = os.getenv("DEBUG", "false").lower() == "true"

.env Files (Local Development)

Create .env File

# .env (local development only)
DATABASE_URL=postgresql://localhost/mydb
API_KEY=dev_key_12345
LOG_LEVEL=debug
PORT=3000

Use with dotenv (Node.js)

npm install dotenv
// Load .env file
require('dotenv').config();

const dbUrl = process.env.DATABASE_URL;

Use with Python

pip install python-dotenv
from dotenv import load_dotenv
import os

load_dotenv()
db_url = os.getenv("DATABASE_URL")

Use with .NET

dotnet add package DotNetEnv
DotNetEnv.Env.Load();
var dbUrl = Environment.GetEnvironmentVariable("DATABASE_URL");

Best Practices

1. Never Commit .env to Git

# .gitignore
.env
.env.local
.env.*.local

2. Use .env.example for Documentation

# .env.example (commit this to show what variables are needed)
DATABASE_URL=
API_KEY=
LOG_LEVEL=debug
PORT=3000

3. Validate on Startup

// Check required variables
const requiredEnvs = ['DATABASE_URL', 'API_KEY'];
requiredEnvs.forEach(env => {
    if (!process.env[env]) {
        throw new Error(`Missing required environment variable: ${env}`);
    }
});

4. Use Descriptive Names

✓ DATABASE_CONNECTION_STRING
✓ AWS_S3_BUCKET_NAME
✓ STRIPE_API_SECRET_KEY

✗ db
✗ bucket
✗ key

5. Don't Log Sensitive Variables

// Bad
console.log(`API Key is: ${process.env.API_KEY}`);

// Good
console.log("App started successfully");

6. Use Secrets Managers in Production

# AWS Secrets Manager
aws secretsmanager get-secret-value --secret-id prod/api-key

# Azure Key Vault
az keyvault secret show --name api-key --vault-name mykeyvault

# HashiCorp Vault
vault kv get secret/data/api-key

Practical Examples

Database Configuration

// config.js
module.exports = {
    database: {
        host: process.env.DB_HOST || 'localhost',
        port: parseInt(process.env.DB_PORT || '5432'),
        name: process.env.DB_NAME || 'myapp',
        user: process.env.DB_USER,
        password: process.env.DB_PASSWORD,
    }
};

Feature Flags

public class FeatureFlags
{
    public bool EnableNewDashboard => 
        bool.Parse(Environment.GetEnvironmentVariable("FEATURE_NEW_DASHBOARD") ?? "false");
    
    public bool EnableBetaFeatures => 
        bool.Parse(Environment.GetEnvironmentVariable("FEATURE_BETA") ?? "false");
}

Environment-Specific Behavior

const isDevelopment = process.env.NODE_ENV === 'development';
const isProduction = process.env.NODE_ENV === 'production';

if (isDevelopment) {
    // Enable detailed logging
    console.log("Debug mode enabled");
} else if (isProduction) {
    // Use secure cookies
    app.set('trust proxy', 1);
}

Real-World Use Cases

  • Local development: Each developer has their own .env with test credentials
  • CI/CD pipelines: GitHub Actions, GitLab CI inject secrets automatically
  • Docker containers: Environment variables passed at runtime
  • Kubernetes: ConfigMaps for non-sensitive, Secrets for sensitive data
  • Cloud platforms: AWS Lambda, Azure Functions, Google Cloud Functions read env vars automatically

Related Concepts to Explore

  • Secrets management (AWS Secrets Manager, Azure Key Vault, HashiCorp Vault)
  • Configuration management systems
  • Feature flags and A/B testing
  • .NET Configuration providers
  • Node.js environment variables
  • Docker and containerization
  • Kubernetes ConfigMaps and Secrets
  • CI/CD pipeline secrets
  • Infrastructure as Code (IaC)
  • Application configuration patterns

Summary

Environment variables are a fundamental DevOps practice that separates configuration from code. By using them properly—keeping secrets out of git, validating on startup, and following naming conventions—you build more secure, flexible, and maintainable applications that work seamlessly across development, testing, and production environments.