DEV Community

Young Gao
Young Gao

Posted on

Environment Variables Done Right: From .env Files to Production Configs

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>;
Enter fullscreen mode Exit fullscreen mode

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)
Enter fullscreen mode Exit fullscreen mode

Loading priority: platform env vars > .env.{NODE_ENV}.local > .env.local > .env.{NODE_ENV} > .env

Common Mistakes

  1. Committing .env.local with real secrets. Add it to .gitignore immediately.
  2. Using process.env directly everywhere. Centralize in one config module.
  3. No type coercion. PORT comes as a string. Parse it to a number.
  4. Boolean gotcha: FEATURE_FLAG=false is 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

Follow me for more production-ready backend content!


If this helped you, buy me a coffee on Ko-fi!

Top comments (0)