If you've ever committed an API key to GitHub or hardcoded a database password in your application code, you've learned the hard way why environment variables secrets management matters. Environment variables separate configuration from code, keeping sensitive credentials out of your repository and making your application portable across development, staging, and production environments.
This guide covers everything from basic .env files to production-ready secrets handling.
What Are Environment Variables?
Environment variables are key-value pairs that live outside your application code. Instead of hardcoding DB_PASSWORD=mysecretpassword in your source files, you set it as an environment variable that your application reads at runtime.
# Set an environment variable
export DATABASE_URL="postgres://user:pass@localhost:5432/myapp"
# Your application reads it
echo $DATABASE_URL
This separation provides three critical benefits:
- Security — credentials never appear in your codebase or version control
- Portability — the same code runs in development, staging, and production with different configs
- Flexibility — change configuration without redeploying code
The .env File Pattern
Most frameworks use .env files for local development. This file sits in your project root and is loaded into environment variables at startup.
Basic .env File
# Database
DB_HOST=localhost
DB_PORT=5432
DB_NAME=myapp
DB_USER=developer
DB_PASSWORD=local_dev_password
# Application
APP_ENV=development
APP_PORT=3000
APP_SECRET=dev-secret-key-not-for-production
# External services
STRIPE_SECRET_KEY=sk_test_xxxxxxxxxxxxx
SENDGRID_API_KEY=SG.xxxxxxxxxxxxxxxxxxxx
AWS_ACCESS_KEY_ID=AKIAXXXXXXXXXXXXXXXXX
AWS_SECRET_ACCESS_KEY=xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
Critical Rule: Never Commit .env Files
Add .env to your .gitignore immediately:
# .gitignore
.env
.env.local
.env.production
.env.*.local
Instead, commit a .env.example file with placeholder values:
# .env.example — commit this to show required variables
DB_HOST=localhost
DB_PORT=5432
DB_NAME=
DB_USER=
DB_PASSWORD=
APP_SECRET=
STRIPE_SECRET_KEY=
New team members copy .env.example to .env and fill in their own values.
Environment Variables Secrets Management by Framework
Node.js
npm install dotenv
// Load at the very top of your entry file
require('dotenv').config();
// Access variables
const dbHost = process.env.DB_HOST;
const apiKey = process.env.STRIPE_SECRET_KEY;
// Validate required variables on startup
const required = ['DB_HOST', 'DB_PASSWORD', 'APP_SECRET'];
for (const key of required) {
if (!process.env[key]) {
console.error(`Missing required env var: ${key}`);
process.exit(1);
}
}
Laravel (PHP)
Laravel has built-in .env support:
// Access variables
$dbHost = env('DB_HOST', 'localhost'); // with default fallback
$apiKey = config('services.stripe.secret'); // through config files
// config/services.php
'stripe' => [
'secret' => env('STRIPE_SECRET_KEY'),
],
Important for production: Run php artisan config:cache after setting up your environment. This compiles all config into a single cached file. If you've followed our Laravel deployment checklist, this is already part of your deploy process.
Python (Django/Flask)
pip install python-dotenv
from dotenv import load_dotenv
import os
load_dotenv()
DATABASE_URL = os.getenv('DATABASE_URL')
SECRET_KEY = os.getenv('APP_SECRET')
DEBUG = os.getenv('APP_ENV') == 'development'
Production Secrets Management
.env files work for development, but production needs more robust solutions. Here's how to handle secrets securely on a VPS.
Method 1: Server-Level Environment Variables
Set variables directly on your server so they persist across reboots:
# Add to /etc/environment (system-wide)
echo 'DB_PASSWORD=production_password_here' | sudo tee -a /etc/environment
# Or add to the application user's profile
echo 'export DB_PASSWORD=production_password_here' >> ~/.bashrc
source ~/.bashrc
For applications managed by systemd:
# /etc/systemd/system/myapp.service
[Service]
Environment="DB_HOST=localhost"
Environment="DB_PASSWORD=production_password"
Environment="APP_ENV=production"
EnvironmentFile=/etc/myapp/env
Method 2: Encrypted .env Files
Encrypt your production .env file so it can be safely stored:
# Encrypt with OpenSSL
openssl enc -aes-256-cbc -salt -in .env.production -out .env.production.enc -pass pass:your-encryption-key
# Decrypt during deployment
openssl enc -aes-256-cbc -d -in .env.production.enc -out .env -pass pass:your-encryption-key
Store the encrypted file in your repository. The decryption key is the only secret you need to manage separately.
Method 3: CI/CD Secrets
When using GitHub Actions or similar CI/CD tools, store secrets in the platform's secret manager:
# .github/workflows/deploy.yml
jobs:
deploy:
runs-on: ubuntu-latest
steps:
- name: Deploy to server
uses: appleboy/ssh-action@v1
with:
host: ${{ secrets.SERVER_HOST }}
username: ${{ secrets.SERVER_USER }}
key: ${{ secrets.SSH_PRIVATE_KEY }}
script: |
cd /var/www/myapp
git pull origin main
echo "DB_PASSWORD=${{ secrets.DB_PASSWORD }}" > .env
echo "APP_SECRET=${{ secrets.APP_SECRET }}" >> .env
This approach keeps secrets in GitHub's encrypted vault and injects them during deployment. For complex setups with staging environments, you can use separate secret sets for each environment.
Common Environment Variables Secrets Management Mistakes
Mistake 1: Committing Secrets to Git
Once a secret is in your Git history, it's there forever — even if you delete the file later. If this happens:
# Remove the file from Git history (use with caution)
git filter-branch --force --index-filter \
'git rm --cached --ignore-unmatch .env' \
--prune-empty --tag-name-filter cat -- --all
Then immediately rotate all exposed credentials.
Mistake 2: Using the Same Secrets Everywhere
Development, staging, and production should use different credentials. A leaked development API key shouldn't grant access to production data.
Mistake 3: Not Validating on Startup
Your application should fail fast if required environment variables are missing. Silent failures when a variable is undefined lead to confusing runtime errors.
Mistake 4: Logging Secrets
Never log environment variables or include them in error messages:
// Bad — exposes secrets in logs
console.log('Config:', process.env);
// Good — log only non-sensitive config
console.log('Environment:', process.env.APP_ENV);
console.log('Port:', process.env.APP_PORT);
Security Best Practices
Rotate secrets regularly. Change API keys and passwords every 90 days. Automate this where possible.
Use least privilege. Database users should only have permissions they need. API keys should be scoped to required operations.
Separate secrets by environment. Production, staging, and development should never share credentials.
Audit access. Know who has access to production secrets and review regularly.
Use strong, random values. Generate secrets with:
# Generate a random 64-character secret
openssl rand -hex 32
# Or base64 encoded
openssl rand -base64 48
FAQ
Should I use .env files in production?
For simple VPS deployments, .env files work fine if properly secured (600 permissions, not in web root). For larger deployments, use systemd EnvironmentFile, cloud secret managers, or encrypted environment files.
What happens if I accidentally commit a secret to GitHub?
Assume it's compromised. Immediately rotate the credential, remove it from Git history using git filter-branch or BFG Repo-Cleaner, and force-push. GitHub also scans for known secret patterns and may alert you.
How do I share secrets with my team?
Never share via email or chat. Use a password manager (1Password, Bitwarden) with shared vaults, or use encrypted .env files with a shared decryption key stored in the password manager.
Do I need a dedicated secrets manager like HashiCorp Vault?
For small to medium projects on a VPS, .env files with proper file permissions and CI/CD secrets are sufficient. Dedicated secret managers like Vault add value when you have many services, automatic rotation requirements, or compliance needs.
Secure Your Application on DeployBase
Proper environment variables secrets management protects your application, your users, and your business. Combined with secure hosting infrastructure, it forms the foundation of a trustworthy application.
At DeployBase, our VPS plans give you full SSH access to configure environment variables, set file permissions, and manage secrets exactly as described in this guide. Starting at $5/month with NVMe SSD storage, automated backups, and 24/7 support.
Get your VPS at DeployBase → — secure hosting for secure applications.




