Configuration Files
While environment variables handle simple configuration, complex applications often need configuration files. This lesson covers strategies for managing config files in Docker.
Configuration Strategies
┌─────────────────────────────────────────────────────────────────┐
│ Configuration Methods │
├─────────────────────────────────────────────────────────────────┤
│ │
│ 1. Baked into image → Static, requires rebuild │
│ 2. Environment vars → Simple values, runtime │
│ 3. Bind mounts → Development, local files │
│ 4. Volumes → Persistent, shared configs │
│ 5. Docker configs → Swarm mode, versioned │
│ 6. ConfigMaps → Kubernetes │
│ │
└─────────────────────────────────────────────────────────────────┘
Config Files in Images
For static configuration that rarely changes:
FROM nginx:alpine
# Copy config into image
COPY nginx.conf /etc/nginx/nginx.conf
COPY sites/ /etc/nginx/conf.d/
Pros and Cons
| Pros | Cons |
|---|---|
| Simple deployment | Requires rebuild for changes |
| Immutable | Different configs need different images |
| Version controlled | Not suitable for secrets |
Bind Mount Configuration
Mount config files from the host:
# Mount single file
docker run -v $(pwd)/nginx.conf:/etc/nginx/nginx.conf:ro nginx
# Mount config directory
docker run -v $(pwd)/config:/app/config:ro myapp
Docker Compose
services:
nginx:
image: nginx:alpine
volumes:
- ./nginx.conf:/etc/nginx/nginx.conf:ro
- ./sites:/etc/nginx/conf.d:ro
api:
image: myapi
volumes:
- ./config:/app/config:ro
Configuration Templates
Generate config files from environment variables:
Using envsubst
# template.conf
server {
listen ${NGINX_PORT};
server_name ${SERVER_NAME};
}
FROM nginx:alpine
COPY template.conf /etc/nginx/template.conf
COPY docker-entrypoint.sh /docker-entrypoint.sh
ENTRYPOINT ["/docker-entrypoint.sh"]
CMD ["nginx", "-g", "daemon off;"]
#!/bin/sh
# docker-entrypoint.sh
envsubst < /etc/nginx/template.conf > /etc/nginx/nginx.conf
exec "$@"
Using confd
FROM mybase
# Install confd
ADD https://github.com/kelseyhightower/confd/releases/download/v0.16.0/confd-0.16.0-linux-amd64 /usr/local/bin/confd
RUN chmod +x /usr/local/bin/confd
COPY confd/ /etc/confd/
Docker Configs (Swarm Mode)
Docker configs provide versioned, immutable configuration:
# Create config
docker config create nginx-config nginx.conf
# List configs
docker config ls
# Use in service
docker service create \
--name web \
--config source=nginx-config,target=/etc/nginx/nginx.conf \
nginx
Docker Compose with Configs
services:
nginx:
image: nginx:alpine
configs:
- source: nginx_config
target: /etc/nginx/nginx.conf
- source: app_config
target: /etc/nginx/conf.d/app.conf
configs:
nginx_config:
file: ./nginx.conf
app_config:
file: ./app.conf
Multi-Environment Configuration
Directory Structure
config/
├── default.json
├── development.json
├── staging.json
└── production.json
Environment-Based Loading
services:
api:
image: myapi
environment:
NODE_ENV: ${ENVIRONMENT:-development}
volumes:
- ./config:/app/config:ro
// config/index.js
const env = process.env.NODE_ENV || 'development';
const defaultConfig = require('./default.json');
const envConfig = require(`./${env}.json`);
module.exports = { ...defaultConfig, ...envConfig };
Override Pattern
# docker-compose.yml (base)
services:
api:
image: myapi
volumes:
- ./config/default.yml:/app/config.yml:ro
# docker-compose.override.yml (development)
services:
api:
volumes:
- ./config/development.yml:/app/config.yml:ro
# docker-compose.prod.yml (production)
services:
api:
volumes:
- ./config/production.yml:/app/config.yml:ro
Application-Specific Patterns
Nginx Configuration
services:
nginx:
image: nginx:alpine
volumes:
- ./nginx/nginx.conf:/etc/nginx/nginx.conf:ro
- ./nginx/conf.d:/etc/nginx/conf.d:ro
- ./nginx/ssl:/etc/nginx/ssl:ro
environment:
- NGINX_ENVSUBST_OUTPUT_DIR=/etc/nginx/conf.d
PostgreSQL Configuration
services:
postgres:
image: postgres:15
volumes:
- ./postgres/postgresql.conf:/etc/postgresql/postgresql.conf:ro
- ./postgres/pg_hba.conf:/etc/postgresql/pg_hba.conf:ro
command: postgres -c config_file=/etc/postgresql/postgresql.conf
Redis Configuration
services:
redis:
image: redis:alpine
volumes:
- ./redis/redis.conf:/usr/local/etc/redis/redis.conf:ro
command: redis-server /usr/local/etc/redis/redis.conf
Node.js Application
services:
api:
build: .
volumes:
- ./config:/app/config:ro
environment:
NODE_ENV: production
CONFIG_DIR: /app/config
Init Scripts
Run initialization scripts at container start:
PostgreSQL
services:
postgres:
image: postgres:15
volumes:
- ./init:/docker-entrypoint-initdb.d:ro
-- init/01-schema.sql
CREATE TABLE users (...);
-- init/02-seed.sql
INSERT INTO users ...;
Custom Init Pattern
FROM mybase
COPY init.d/ /docker-entrypoint.d/
COPY docker-entrypoint.sh /docker-entrypoint.sh
ENTRYPOINT ["/docker-entrypoint.sh"]
#!/bin/bash
# docker-entrypoint.sh
# Run all init scripts
for script in /docker-entrypoint.d/*.sh; do
[ -f "$script" ] && source "$script"
done
# Start main process
exec "$@"
Config Validation
Validate configuration before starting:
FROM nginx:alpine
COPY nginx.conf /etc/nginx/nginx.conf
# Validate config at build time
RUN nginx -t
CMD ["nginx", "-g", "daemon off;"]
services:
api:
build: .
healthcheck:
test: ["CMD", "node", "validate-config.js"]
interval: 10s
start_period: 5s
Watching for Config Changes
Development with Hot Reload
services:
api:
volumes:
- ./config:/app/config:ro
environment:
- WATCH_CONFIG=true
Production Config Reload
services:
nginx:
volumes:
- ./nginx.conf:/etc/nginx/nginx.conf:ro
# Reload nginx config without restart
docker exec nginx nginx -s reload
Best Practices
1. Use Read-Only Mounts
volumes:
- ./config:/app/config:ro
2. Separate Static and Dynamic Config
config/
├── static/ # Baked into image
└── dynamic/ # Mounted at runtime
3. Validate Early
# Check config at build time
RUN npm run validate:config
4. Document Configuration
# config/schema.json
{
"type": "object",
"required": ["database", "api"],
"properties": {
"database": {
"type": "object",
"required": ["host", "port"]
}
}
}
5. Default to Sensible Values
const config = {
port: process.env.PORT || 3000,
logLevel: process.env.LOG_LEVEL || 'info',
...loadConfigFile(),
};
Key Takeaways
- Bake static config into images, mount dynamic config
- Use read-only mounts for security
- Templates with envsubst enable environment-specific config
- Docker configs provide versioned configuration in Swarm
- Validate configuration at build time and startup
- Use init scripts for database schema and seeding
- Separate configuration by environment (dev/staging/prod)
- Enable hot reload for development, graceful reload for production

