Environment Variables Best Practices: Keeping Secrets Safe
Environment variables are the standard way to manage configuration and secrets in modern applications. Whether it’s a database connection string, an API key, or a JWT secret, these values should never be hardcoded in source code. This guide covers best practices for managing environment variables securely across development, staging, and production environments.
Why Not Hardcode Secrets?
Hardcoded secrets in source code get committed to version control. Even if you later remove them, the secret remains in git history. Public repositories on GitHub are routinely scanned for API keys by automated bots that exploit them within seconds of discovery. Even private repos present risk if access is ever compromised.
The .env File Pattern
The standard development approach uses a .env file to store environment variables locally. Libraries like dotenv (Node.js), python-dotenv, and similar packages load these values at startup. The critical rule: always add .env to your .gitignore file. Commit a .env.example file with placeholder values and documentation for each variable.
Secrets Management in Production
In production, never use .env files. Use a proper secrets management solution:
- AWS Secrets Manager / Parameter Store — Managed secrets with rotation, versioning, and IAM access control
- HashiCorp Vault — Open-source secrets management with dynamic secret generation
- Kubernetes Secrets — Built-in secret management for containerized workloads
- Docker Swarm Secrets — Encrypted secret management for Swarm services
Naming Conventions
Use SCREAMING_SNAKE_CASE for environment variable names. Prefix variables by service for clarity: DATABASE_URL, REDIS_URL, STRIPE_SECRET_KEY, JWT_SECRET. Prefix environment-specific variables: PROD_DB_URL vs DEV_DB_URL if you manage multiple environments in the same config.
Validating Environment Variables at Startup
Use a schema validation library to validate required environment variables when your application starts. Libraries like Zod (Node.js) or Pydantic (Python) can define a schema for your config and throw a clear error if a required variable is missing or has an invalid value — preventing mysterious runtime failures caused by missing config.
Auditing and Rotation
Periodically audit which services have access to which secrets. Rotate secrets regularly and immediately if a breach is suspected. Store only the minimum necessary permissions in each secret — don’t use a single root database credential for all services.
Working with encoded secrets? Use the Base64 Encoder/Decoder on devutilitypro.com to encode and decode environment variable values when working with APIs that require Base64 encoding.