0

Environment variables done right

I've seen API keys committed to Git, .env files with 50+ variables, and production secrets stored in plain text files. Here's how to manage environment variables properly.

The Basics

# .env file (development only)
DATABASE_URL=postgresql://localhost:5432/mydb
REDIS_URL=redis://localhost:6379
API_KEY=dev_key_123
PORT=3000
// Load with dotenv (Node.js)
require('dotenv').config();
const dbUrl = process.env.DATABASE_URL;

Rule #1: .env goes in .gitignore. Always.

Structure Your Variables

# .env.example (committed to Git — no real values)
DATABASE_URL=
REDIS_URL=
API_KEY=
PORT=3000

# .env.development
DATABASE_URL=postgresql://localhost:5432/mydb_dev

# .env.test
DATABASE_URL=postgresql://localhost:5432/mydb_test

# .env.production (never in Git)
DATABASE_URL=postgresql://prod-host:5432/mydb_prod

Validation at Startup

Don't wait for the first database call to discover DATABASE_URL is missing.

// config.js
const required = ['DATABASE_URL', 'REDIS_URL', 'API_KEY'];
const missing = required.filter(key => !process.env[key]);

if (missing.length > 0) {
    console.error(`Missing env vars: ${missing.join(', ')}`);
        process.exit(1);
        }
        
        module.exports = {
            db: process.env.DATABASE_URL,
                redis: process.env.REDIS_URL,
                    apiKey: process.env.API_KEY,
                        port: parseInt(process.env.PORT || '3000'),
                            isDev: process.env.NODE_ENV !== 'production',
                            };
                            ```
                            
                            ## Secrets Management (Production)
                            
                            ### Option 1: Platform secrets
                            
                            ```yaml
                            # GitHub Actions
                            env:
                              API_KEY: ${{ secrets.API_KEY }}
                              
                              # Docker Compose
                              services:
                                app:
                                    environment:
                                          - API_KEY=${API_KEY}  # From host environment
                                          ```
                                          
                                          ### Option 2: Vault/AWS Secrets Manager
                                          
                                          ```python
                                          import boto3
                                          
                                          def get_secret(name):
                                              client = boto3.client('secretsmanager')
                                                  response = client.get_secret_value(SecretId=name)
                                                      return json.loads(response['SecretString'])
                                                      
                                                      secrets = get_secret('myapp/production')
                                                      db_url = secrets['DATABASE_URL']
                                                      ```
                                                      
                                                      ### Option 3: Encrypted .env
                                                      
                                                      ```bash
                                                      # Encrypt
                                                      gpg --symmetric --cipher-algo AES256 .env.production
                                                      # Creates .env.production.gpg (safe to commit)
                                                      
                                                      # Decrypt
                                                      gpg --decrypt .env.production.gpg > .env.production
                                                      ```
                                                      
                                                      ## Common Mistakes
                                                      
                                                      1. **Default values for secrets**: `API_KEY || 'default'` — fails silently in production
                                                      2. **Logging env vars**: `console.log(config)` — prints secrets to log files
                                                      3. **Different names across environments**: `DB_URL` vs `DATABASE_URL` vs `DB_CONNECTION_STRING`
                                                      4. **Putting non-secrets in env vars**: App name, feature flags → use config files instead
                                                      
                                                      ## Docker
                                                      
                                                      ```dockerfile
                                                      # Bad: bakes secrets into image layer
                                                      ENV API_KEY=secret123
                                                      
                                                      # Good: pass at runtime
                                                      # docker run -e API_KEY=secret123 myapp
                                                      # or: docker run --env-file .env myapp
                                                      ```
                                                      
                                                      ---
                                                      
                                                      How do you manage secrets in production? Always curious about different setups.

All Rights Reserved

Viblo
Let's register a Viblo Account to get more interesting posts.