Configuration in Docker
Applications should be configured through the environment, not hardcoded values. This follows the 12-Factor App methodology and makes your containers portable across development, staging, and production environments.
Setting Environment Variables
In docker run
# Individual variables
docker run -e NODE_ENV=production -e PORT=3000 myapp
# From a file
docker run --env-file .env myapp
# From host environment
export API_KEY=sk-123
docker run -e API_KEY myapp # Passes host's API_KEY
In Dockerfile
# Set default values (can be overridden at runtime)
ENV NODE_ENV=production
ENV PORT=3000
# Use build arguments for build-time variables
ARG VERSION=1.0.0
ENV APP_VERSION=${VERSION}
# ❌ NEVER put secrets in Dockerfile
# ENV API_KEY=sk-secret-key
In Docker Compose
# docker-compose.yml
services:
api:
build: .
environment:
# Direct values
NODE_ENV: production
PORT: "4000"
# From .env file variables
DATABASE_URL: postgres://${DB_USER}:${DB_PASS}@db:5432/${DB_NAME}
REDIS_URL: redis://redis:6379
# Load from .env file
env_file:
- .env
- .env.local # Overrides (not committed to git)
The .env File
# .env — Docker Compose auto-loads this
NODE_ENV=production
PORT=4000
# Database
DB_USER=admin
DB_PASS=supersecret
DB_NAME=myapp
DB_HOST=db
DB_PORT=5432
# External services
REDIS_URL=redis://redis:6379
API_KEY=sk-your-api-key
# App settings
LOG_LEVEL=info
CORS_ORIGIN=https://myapp.com
⚠️ Security Warning
Never commit .env files with real secrets to version control!
# .gitignore
.env
.env.local
.env.production
*.pem
Instead, commit a .env.example with placeholder values:
# .env.example — commit this
NODE_ENV=development
DB_USER=admin
DB_PASS=change-me
API_KEY=your-api-key-here
Multi-Environment Configuration
# docker-compose.yml (base)
services:
api:
build: .
env_file: .env
# docker-compose.dev.yml
services:
api:
env_file:
- .env
- .env.development
volumes:
- ./src:/app/src
# docker-compose.prod.yml
services:
api:
env_file:
- .env
- .env.production
restart: always
# Run with specific environment
docker compose -f docker-compose.yml -f docker-compose.dev.yml up
docker compose -f docker-compose.yml -f docker-compose.prod.yml up -d
Accessing Environment Variables in Code
// Node.js
const config = {
port: parseInt(process.env.PORT || '3000'),
nodeEnv: process.env.NODE_ENV || 'development',
databaseUrl: process.env.DATABASE_URL,
redisUrl: process.env.REDIS_URL,
apiKey: process.env.API_KEY,
};
// Validate required variables
const required = ['DATABASE_URL', 'API_KEY'];
for (const key of required) {
if (!process.env[key]) {
console.error(`Missing required environment variable: ${key}`);
process.exit(1);
}
}
Key Takeaways
- ✅ Use environment variables for all configuration — never hardcode
- ✅ Use
.envfiles for local development - ✅ Never commit secrets to version control
- ✅ Validate required environment variables at startup
- ✅ Use different compose override files for each environment