What Is a .env File and Why Is It Missing in Production?
The Mystery of the Missing .env File
You've built your app locally, everything works perfectly, and then you deploy to production only to see your app crash with cryptic environment variable errors. Sound familiar? Welcome to the classic ".env file isn't in production" puzzle that trips up developers constantly.
Let's break down what .env files are, why they disappear in production, and how to handle environment variables like a pro.
What Exactly Is a .env File?
A .env file is a simple text file that stores environment variables as key-value pairs. It's the standard way to keep sensitive configuration data (like API keys, database URLs, and secrets) out of your source code.
Here's what a typical .env file looks like:
DATABASE_URL=postgresql://user:password@localhost:5432/myapp
API_KEY=sk_live_abcd1234567890
JWT_SECRET=super-secret-key-dont-share
NODE_ENV=development
PORT=3000
Your application reads these variables at runtime using libraries like dotenv in Node.js:
require('dotenv').config();
const dbUrl = process.env.DATABASE_URL;
const apiKey = process.env.API_KEY;
Why .env Files Don't Belong in Production
Here's the thing: .env files are a development convenience, not a production deployment strategy. There are several reasons why your .env file isn't (and shouldn't be) in production:
1. .gitignore Keeps Them Local
Your .env file should be listed in .gitignore to prevent accidentally committing secrets to your repository:
# .gitignore
.env
.env.local
.env.*.local
This means when your code gets deployed from Git, the .env file stays behind on your local machine.
2. Security Best Practices
Shipping .env files to production is a security anti-pattern. Production secrets should be managed through proper secret management systems, not plain text files sitting on your server.
3. Environment Separation
Your development environment variables are different from production. You don't want your prod app connecting to your local database or using test API keys.
How to Handle Environment Variables in Production
So if .env files don't go to production, how do you get your environment variables there? Here are the proper approaches:
Platform Environment Variables
Most hosting platforms provide ways to set environment variables through their dashboard or CLI:
Vercel:
vercel env add API_KEY
Netlify:
netlify env:set API_KEY "your-key-here"
Railway:
railway variables set API_KEY=your-key-here
Container Environment Variables
If you're using Docker, you can pass environment variables at runtime:
docker run -e DATABASE_URL="postgresql://..." -e API_KEY="sk_live_..." myapp
Or use an environment file (different from .env):
docker run --env-file production.env myapp
CI/CD Pipeline Secrets
For GitHub Actions, GitLab CI, or other CI/CD systems, store secrets in their secret management systems:
# GitHub Actions
- name: Deploy
env:
DATABASE_URL: ${{ secrets.DATABASE_URL }}
API_KEY: ${{ secrets.API_KEY }}
run: npm run deploy
Common Gotchas and Solutions
Missing NODE_ENV
One of the most common issues is forgetting to set NODE_ENV=production. Many frameworks behave differently between development and production modes.
# Always set this in production
NODE_ENV=production
Case Sensitivity
Environment variable names are case-sensitive. api_key is not the same as API_KEY. Stick to UPPER_SNAKE_CASE for consistency.
Default Values
Always provide sensible defaults in your code:
const port = process.env.PORT || 3000;
const nodeEnv = process.env.NODE_ENV || 'development';
Runtime vs Build Time
Some variables are needed at build time, others at runtime. Know the difference:
// Build time (for Next.js)
NEXT_PUBLIC_API_URL=https://api.example.com
// Runtime
DATABASE_URL=postgresql://...
Best Practices for Environment Management
1. Use a .env.example File
Create a template that shows what variables are needed:
# .env.example
DATABASE_URL=postgresql://user:password@localhost:5432/dbname
API_KEY=your_api_key_here
JWT_SECRET=your_jwt_secret_here
NODE_ENV=development
2. Validate Required Variables
Fail fast if critical environment variables are missing:
const requiredEnvVars = ['DATABASE_URL', 'API_KEY', 'JWT_SECRET'];
for (const envVar of requiredEnvVars) {
if (!process.env[envVar]) {
throw new Error(`Missing required environment variable: ${envVar}`);
}
}
3. Use Different .env Files for Different Environments
.env.development
.env.staging
.env.production.example
4. Never Log Sensitive Variables
// DON'T DO THIS
console.log('Config:', process.env);
// DO THIS
console.log('App starting on port:', process.env.PORT);
Debugging Environment Variable Issues
When your app breaks in production due to missing environment variables, here's how to debug:
- Check your hosting platform's environment variable settings
- Verify variable names match exactly (case-sensitive)
- Ensure all required variables are set
- Check if variables need to be available at build time vs runtime
- Look at your application logs for specific error messages
The Bottom Line
.env files are amazing for local development, but they're not meant for production. Understanding how to properly manage environment variables across different environments is crucial for any developer shipping apps to production.
The key is treating environment variables as a deployment concern, not a development convenience. Use your hosting platform's built-in environment variable management, keep secrets out of your code, and always have a clear separation between development and production configurations.
Remember: if your app works locally but breaks in production, environment variables are usually the first place to look. Master this concept, and you'll save yourself hours of debugging headaches.
Alex Hackney
DeployMyVibe