Environment Variables Done Right: From .env Files to Production Configs
Hardcoded config values are the fastest way to ship broken code to the wrong environment. Here is how to manage configuration properly.
The Config Module Pattern
import { z } from "zod";
const envSchema = z.object({
NODE_ENV: z.enum(["development", "staging", "production"]).default("development"),
PORT: z.coerce.number().default(3000),
DATABASE_URL: z.string().url(),
REDIS_URL: z.string().url(),
JWT_SECRET: z.string().min(32),
LOG_LEVEL: z.enum(["debug", "info", "warn", "error"]).default("info"),
});
const parsed = envSchema.safeParse(process.env);
if (\!parsed.success) {
console.error("Invalid environment variables:", parsed.error.flatten());
process.exit(1);
}
export const config = parsed.data;
export type Config = z.infer<typeof envSchema>;
Why Validation at Startup
Without validation, a missing DATABASE_URL crashes your app at the first query, not at startup. By then your health check passed, load balancer routed traffic, and users see 500 errors. Validate early, fail fast.
Environment-Specific Overrides
.env # Shared defaults (committed)
.env.local # Local overrides (gitignored)
.env.production # Production values (gitignored)
.env.test # Test values (committed)
Loading priority: platform env vars > .env.{NODE_ENV}.local > .env.local > .env.{NODE_ENV} > .env
Common Mistakes
- Committing .env.local with real secrets. Add it to .gitignore immediately.
- Using process.env directly everywhere. Centralize in one config module.
- No type coercion. PORT comes as a string. Parse it to a number.
-
Boolean gotcha:
FEATURE_FLAG=falseis still truthy as a string. Parse explicitly.
Production: Use Your Platform
In production, never use .env files. Use:
- Docker: env_file or environment in docker-compose, K8s ConfigMap/Secrets
- Cloud: AWS Parameter Store/Secrets Manager, GCP Secret Manager, Azure Key Vault
- CI/CD: GitHub Actions secrets, GitLab CI variables
The .env file is for local development only.
Part of my Production Backend Patterns series. Follow for more practical backend engineering.
You Might Also Like
- Production Secrets Management: From .env Files to HashiCorp Vault (2026 Guide)
- Environment Variables Done Right: Stop Hardcoding Secrets in 2026
- Docker Compose for Development: The Setup Every Backend Dev Needs
Follow me for more production-ready backend content!
If this helped you, buy me a coffee on Ko-fi!
Top comments (0)