Les variables d'environnement sont le principal mecanisme de configuration des conteneurs dans Docker Compose. Mais il existe 4 facons differentes de les transmettre, chacune avec des comportements, des implications de securite et des pieges distincts. Ce guide couvre toutes les approches, des simples fichiers .env aux secrets Docker de production.
4 facons de passer des variables d'environnement dans Docker Compose
Docker Compose prend en charge quatre methodes distinctes pour injecter des variables d'environnement dans les conteneurs.
1. En ligne avec la directive environment
Definissez des paires cle-valeur directement dans votre docker-compose.yml. Ideal pour la configuration non sensible et specifique au service.
services:
api:
image: node:20-alpine
environment:
NODE_ENV: production
DB_HOST: postgres
DB_PORT: "5432"
LOG_LEVEL: info
# List syntax (also valid)
worker:
image: node:20-alpine
environment:
- NODE_ENV=production
- QUEUE_NAME=tasks
- CONCURRENCY=42. Fichier externe avec env_file
Chargez des variables depuis un ou plusieurs fichiers externes. Ideal pour gerer des groupes de variables et garder les secrets hors du controle de version.
services:
api:
image: node:20-alpine
env_file:
- .env # Shared variables
- .env.api # Service-specific variables
- .env.local # Local overrides (gitignored)
db:
image: postgres:16
env_file:
- .env
- .env.db3. Transmission des variables shell
Passez des variables du shell hote au conteneur. Ideal pour les pipelines CI/CD et les valeurs dynamiques.
# In your shell:
export API_KEY=sk-abc123
export DEBUG=true
# docker-compose.yml
services:
api:
image: node:20-alpine
environment:
- API_KEY # Passes host $API_KEY into container
- DEBUG # Passes host $DEBUG into container
- CI # Passes host $CI (empty string if unset)4. Substitution de variables dans docker-compose.yml
Interpolez les variables d'environnement hote dans les valeurs de votre fichier Compose.
# .env file
POSTGRES_VERSION=16
APP_PORT=3000
REPLICAS=3
# docker-compose.yml
services:
db:
image: postgres:${POSTGRES_VERSION} # Resolved from .env or shell
app:
ports:
- "${APP_PORT}:3000" # Resolved from .env or shell
deploy:
replicas: ${REPLICAS} # Resolved from .env or shellTableau comparatif
| Methode | Ideal pour | Dans VCS ? | Secret-safe ? |
|---|---|---|---|
| environment | Non-sensitive defaults | Yes | No |
| env_file | Grouped config, secrets | No (.gitignore) | Partial |
| Shell passthrough | CI/CD, dynamic values | N/A | Partial |
| ${} substitution | Compose file templating | Yes (file), No (.env) | No |
Fichier .env : syntaxe, emplacement et interpolation
Docker Compose lit automatiquement un fichier .env dans le meme repertoire que votre docker-compose.yml.
Regles de syntaxe
# Comments start with #
# Blank lines are ignored
# Simple key=value (no spaces around =)
DB_HOST=localhost
DB_PORT=5432
# Values with spaces need quotes
APP_NAME="My Docker App"
GREETING='Hello World'
# Multi-line values (double quotes only)
PRIVATE_KEY="-----BEGIN RSA PRIVATE KEY-----
MIIEpAIBAAKCAQEA...
-----END RSA PRIVATE KEY-----"
# Variable expansion (Compose v2.20+)
BASE_URL=https://api.example.com
HEALTH_URL=${BASE_URL}/health
# Export prefix is ignored (compatible with shell source)
export SECRET_KEY=mysecretkey
# Empty values
EMPTY_VAR=
ALSO_EMPTY=''Emplacement et priorite
Compose cherche .env dans le repertoire du projet. Vous pouvez le remplacer avec --env-file :
# Default: reads .env from project directory
docker compose up
# Override with --env-file
docker compose --env-file .env.staging up
# Multiple --env-file flags (Compose v2.24+)
docker compose --env-file .env --env-file .env.local up
# Project directory structure:
my-project/
docker-compose.yml # Compose file
.env # Auto-loaded by Compose
.env.staging # Loaded with --env-file
.env.production # Loaded with --env-fileInterpolation dans docker-compose.yml
Les variables de .env sont disponibles pour la substitution dans docker-compose.yml, mais elles ne sont PAS automatiquement injectees dans les conteneurs :
# .env
TAG=3.2.1
EXTERNAL_PORT=8080
# docker-compose.yml
services:
web:
image: myapp:${TAG} # Uses TAG from .env -> myapp:3.2.1
ports:
- "${EXTERNAL_PORT}:80" # Uses EXTERNAL_PORT from .env -> 8080:80
environment:
- TAG # WRONG! This passes host $TAG, not .env TAG
- TAG=${TAG} # RIGHT! Explicitly interpolate .env value
# Key insight:
# .env -> docker-compose.yml interpolation (automatic)
# .env -> container environment (NOT automatic, use env_file)env_file vs environment : differences et pieges
Ces deux directives se ressemblent mais se comportent differemment.
| Directive | Comportement |
|---|---|
| environment: | Inlined in docker-compose.yml. Visible in version control. Supports variable substitution. Overrides env_file. |
| env_file: | Loaded from external file(s). Can be gitignored. No variable substitution inside the file. Lower priority than environment. |
| .env (auto) | Only for Compose file interpolation (${} syntax). NOT injected into containers. Lowest priority. |
Pieges critiques
- 1. Priorite : les valeurs environment ecrasent les valeurs env_file pour la meme cle.
- 2. Le fichier .env (charge automatiquement) sert uniquement a l'interpolation du fichier Compose. Il n'injecte PAS de variables dans les conteneurs.
- 3. Les chemins env_file sont relatifs au fichier docker-compose.yml, pas au repertoire de travail actuel.
- 4. Si un env_file est manquant, Compose echouera. Utilisez required: false (Compose v2.24+) pour le rendre optionnel.
# Precedence demonstration:
# .env.shared
DB_HOST=shared-db
DB_PORT=5432
# docker-compose.yml
services:
api:
env_file:
- .env.shared # DB_HOST=shared-db, DB_PORT=5432
environment:
DB_HOST: override-db # Overrides env_file! DB_HOST=override-db
# DB_PORT not listed here # Keeps env_file value: DB_PORT=5432
# Result inside container:
# DB_HOST=override-db (from environment, overrides env_file)
# DB_PORT=5432 (from env_file)
# Optional env_file (Compose v2.24+):
services:
api:
env_file:
- path: .env.local
required: false # Won't fail if file doesn't existDocker Secrets : secrets bases sur des fichiers pour la production
Les secrets Docker offrent une alternative plus securisee aux variables d'environnement pour les donnees sensibles.
Etape 1 : Creer vos fichiers secrets
# Create secret files (don't commit these!)
echo "SuperSecretPassword123" > db_password.txt
echo "sk-prod-abc123xyz789" > api_key.txt
# Add to .gitignore
echo "*.txt" >> .gitignore
echo "secrets/" >> .gitignore
# Alternative: use a secrets directory
mkdir secrets
echo "SuperSecretPassword123" > secrets/db_password
echo "sk-prod-abc123xyz789" > secrets/api_keyEtape 2 : Definir les secrets dans docker-compose.yml
# docker-compose.yml
services:
db:
image: postgres:16
environment:
POSTGRES_PASSWORD_FILE: /run/secrets/db_password # Note: _FILE suffix
secrets:
- db_password # Grant access to this secret
api:
image: myapp:latest
secrets:
- db_password
- api_key
# Secrets available at:
# /run/secrets/db_password
# /run/secrets/api_key
# Top-level secrets definition
secrets:
db_password:
file: ./secrets/db_password # From local file
api_key:
file: ./secrets/api_key # From local file
# Alternative: use environment variable as source
# jwt_secret:
# environment: JWT_SECRET_VALUE # From host env var (Compose v2.23+)Etape 3 : Lire les secrets dans votre application
# Inside the container, secrets are plain text files:
$ docker compose exec api cat /run/secrets/db_password
SuperSecretPassword123
$ docker compose exec api cat /run/secrets/api_key
sk-prod-abc123xyz789
$ docker compose exec api ls -la /run/secrets/
total 8
-r--r--r-- 1 root root 24 Jan 1 00:00 db_password
-r--r--r-- 1 root root 22 Jan 1 00:00 api_keyEtape 4 : Gerer les secrets dans votre code
// Node.js - Read secret from file
const fs = require('fs');
function getSecret(name) {
const secretPath = `/run/secrets/${name}`;
try {
return fs.readFileSync(secretPath, 'utf8').trim();
} catch (err) {
// Fallback to environment variable (for development)
return process.env[name.toUpperCase()];
}
}
const dbPassword = getSecret('db_password');
const apiKey = getSecret('api_key');# Python - Read secret from file
import os
def get_secret(name: str) -> str:
secret_path = f"/run/secrets/{name}"
try:
with open(secret_path) as f:
return f.read().strip()
except FileNotFoundError:
# Fallback to environment variable
return os.environ.get(name.upper(), "")
db_password = get_secret("db_password")
api_key = get_secret("api_key")Pourquoi les secrets plutot que les variables d'environnement ?
- Les variables d'environnement peuvent fuiter via docker inspect, /proc/environ, les logs d'erreur
- Les secrets sont bases sur des fichiers et accessibles uniquement dans /run/secrets/
- Les secrets prennent en charge le controle d'acces granulaire par service
- De nombreuses bases de donnees supportent nativement le suffixe _FILE pour les fichiers secrets
Substitution de variables : syntaxe ${VAR:-default}
Docker Compose supporte la substitution de variables style shell avec des valeurs par defaut.
| Syntaxe | Resultat |
|---|---|
| ${VAR} | Value of VAR. Error if unset. |
| ${VAR:-default} | Value of VAR if set, otherwise "default". |
| ${VAR-default} | Value of VAR if set (even if empty), otherwise "default". |
| ${VAR:?error msg} | Value of VAR if set, otherwise exit with "error msg". |
| ${VAR?error msg} | Value of VAR if set (even if empty), otherwise exit with "error msg". |
| ${VAR:+replacement} | "replacement" if VAR is set and non-empty, otherwise empty. |
| ${VAR+replacement} | "replacement" if VAR is set (even if empty), otherwise empty. |
Exemple pratique
# docker-compose.yml with variable substitution
services:
web:
image: nginx:${NGINX_VERSION:-1.25-alpine} # Default: 1.25-alpine
ports:
- "${WEB_PORT:-80}:80" # Default: 80
- "${SSL_PORT:-443}:443" # Default: 443
volumes:
- ${CONFIG_PATH:-./nginx.conf}:/etc/nginx/nginx.conf:ro
api:
image: ${REGISTRY:-docker.io}/myapp:${TAG:?TAG is required}
# ^ Fails if TAG is not set
environment:
NODE_ENV: ${NODE_ENV:-development}
LOG_LEVEL: ${LOG_LEVEL:-info}
DATABASE_URL: postgres://${DB_USER:-postgres}:${DB_PASS:?DB_PASS required}@db:5432/${DB_NAME:-myapp}
db:
image: postgres:${PG_VERSION:-16}
volumes:
- ${DATA_DIR:-./data}/postgres:/var/lib/postgresql/dataConfiguration multi-environnement : dev/staging/production
Les projets reels necessitent des configurations differentes. Voici des patterns eprouves.
Pattern 1 : Fichiers override (recommande)
Utilisez un docker-compose.yml de base avec des fichiers override specifiques a l'environnement.
# File structure:
project/
docker-compose.yml # Base configuration
docker-compose.override.yml # Dev overrides (auto-loaded!)
docker-compose.staging.yml # Staging overrides
docker-compose.prod.yml # Production overrides# docker-compose.yml (base)
services:
api:
image: myapp:${TAG:-latest}
restart: unless-stopped
depends_on:
- db
- redis
db:
image: postgres:16
volumes:
- pg-data:/var/lib/postgresql/data
redis:
image: redis:7-alpine
volumes:
pg-data:# docker-compose.override.yml (development - auto-loaded)
services:
api:
build: .
volumes:
- .:/app # Hot reload
- /app/node_modules
ports:
- "3000:3000"
- "9229:9229" # Debugger
environment:
NODE_ENV: development
LOG_LEVEL: debug
DB_HOST: db
db:
ports:
- "5432:5432" # Expose DB for local tools
environment:
POSTGRES_PASSWORD: devpassword # OK for dev only# docker-compose.prod.yml (production)
services:
api:
# No build, no volumes, no debug port
ports:
- "3000:3000"
environment:
NODE_ENV: production
LOG_LEVEL: warn
env_file:
- .env.production
secrets:
- db_password
- api_key
deploy:
replicas: 3
resources:
limits:
cpus: '1.0'
memory: 512M
db:
# No exposed ports
environment:
POSTGRES_PASSWORD_FILE: /run/secrets/db_password
secrets:
- db_password
deploy:
resources:
limits:
cpus: '2.0'
memory: 1G
secrets:
db_password:
file: ./secrets/db_password
api_key:
file: ./secrets/api_key# Usage:
# Development (auto-loads docker-compose.override.yml)
docker compose up
# Staging
docker compose -f docker-compose.yml -f docker-compose.staging.yml up -d
# Production
docker compose -f docker-compose.yml -f docker-compose.prod.yml up -d
# Validate merged config before deploying
docker compose -f docker-compose.yml -f docker-compose.prod.yml configPattern 2 : Fichiers .env specifiques
Utilisez --env-file pour charger differents ensembles de variables par environnement.
# .env.development
TAG=latest
APP_PORT=3000
DB_PASSWORD=devpassword
LOG_LEVEL=debug
REPLICAS=1
# .env.staging
TAG=rc-1.2.3
APP_PORT=3000
DB_PASSWORD=staging-secret-pw
LOG_LEVEL=info
REPLICAS=2
# .env.production
TAG=1.2.3
APP_PORT=3000
DB_PASSWORD=prod-ultra-secret-pw
LOG_LEVEL=warn
REPLICAS=3
# Usage:
docker compose --env-file .env.development up # Dev
docker compose --env-file .env.staging up -d # Staging
docker compose --env-file .env.production up -d # ProductionPattern 3 : Profiles pour services optionnels
Utilisez les profiles Compose pour inclure ou exclure des services par environnement.
# docker-compose.yml
services:
api:
image: myapp:${TAG:-latest}
ports:
- "3000:3000"
db:
image: postgres:16
volumes:
- pg-data:/var/lib/postgresql/data
# Dev-only services
adminer:
image: adminer
ports:
- "8080:8080"
profiles:
- dev # Only starts with --profile dev
mailhog:
image: mailhog/mailhog
ports:
- "1025:1025"
- "8025:8025"
profiles:
- dev # Only starts with --profile dev
# Monitoring (staging + production)
prometheus:
image: prom/prometheus
profiles:
- monitoring # Only starts with --profile monitoring
volumes:
pg-data:
# Usage:
docker compose --profile dev up # Includes adminer + mailhog
docker compose --profile monitoring up -d # Includes prometheus
docker compose --profile dev --profile monitoring up # Both profilesChecklist securite : ce qu'il ne faut JAMAIS mettre dans les variables d'environnement
Les variables d'environnement sont pratiques mais inheremment non securisees pour les donnees sensibles.
| NE JAMAIS faire | Faire plutot |
|---|---|
| Coder en dur les mots de passe dans docker-compose.yml | Utiliser des fichiers .env (gitignored) ou Docker secrets |
| Commiter les fichiers .env dans le controle de version | Ajouter .env au .gitignore, commiter .env.example |
| Utiliser les variables d'environnement pour les cles privees | Les monter comme fichiers via volumes ou Docker secrets |
| Journaliser les variables d'environnement dans le code | Masquer les valeurs sensibles dans les logs |
| Partager les fichiers .env par Slack ou email | Utiliser un gestionnaire de secrets (Vault, AWS Secrets Manager) |
| Utiliser les memes secrets en dev/staging/production | Generer des secrets uniques par environnement |
# .gitignore — essential entries for Docker projects
.env
.env.*
!.env.example
secrets/
*.pem
*.key
*.crt# .env.example — commit this as a template
# Database
DB_HOST=localhost
DB_PORT=5432
DB_USER=myapp
DB_PASSWORD=CHANGE_ME
# API Keys
API_KEY=CHANGE_ME
JWT_SECRET=CHANGE_ME
# Application
NODE_ENV=development
LOG_LEVEL=debug
APP_PORT=3000Debogage : pourquoi ma variable d'environnement ne se charge pas ?
Suivez ce processus systematique quand une variable d'environnement n'atteint pas votre conteneur.
Etape 1 : La variable est-elle definie ?
Verifiez qu'elle existe dans votre fichier .env, env_file ou directive environment.
Etape 2 : Atteint-elle le conteneur ?
Executez docker compose exec <service> env pour voir toutes les variables.
Etape 3 : Le fichier .env est-il au bon endroit ?
Le fichier .env doit etre dans le meme repertoire que docker-compose.yml.
Etape 4 : Y a-t-il un conflit de noms ?
Verifiez les fautes de frappe, la casse et la priorite.
Etape 5 : Le fichier Compose est-il valide ?
Executez docker compose config pour voir la configuration resolue.
Etape 6 : La variable est-elle surchargee ?
Les variables shell du hote ecrasent les valeurs .env. Verifiez avec echo $VAR_NAME.
Commandes de debogage utiles
# 1. See the fully resolved Compose config (all variables substituted)
docker compose config
# 2. Check environment variables inside a running container
docker compose exec api env
# 3. Check a specific variable
docker compose exec api sh -c 'echo $DATABASE_URL'
# 4. Inspect container config (shows env vars, mounts, etc.)
docker inspect <container_id> --format '{{json .Config.Env}}' | jq .
# 5. Check if .env file is being read
docker compose config --format json | jq '.services.api.environment'
# 6. Verify env_file exists and is readable
docker compose config 2>&1 | grep -i "env_file"
# 7. Run a one-off container to test environment
docker compose run --rm api env | sort
# 8. Check for shell variable conflicts
env | grep -i "DB_"
# 9. Validate Compose file syntax
docker compose config --quiet && echo "Valid" || echo "Invalid"
# 10. Show variable substitution warnings
docker compose --verbose up 2>&1 | grep -i "variable"Questions frequentes
Quelle est la difference entre .env et env_file dans Docker Compose ?
Le fichier .env est lu automatiquement pour la substitution dans docker-compose.yml. La directive env_file charge les variables directement dans l'environnement du conteneur. Ils servent des objectifs differents.
Comment passer des variables d'environnement du hote au conteneur ?
Listez le nom de la variable sans valeur dans la directive environment ou utilisez la syntaxe ${MY_VAR} dans docker-compose.yml.
Les variables d'environnement Docker sont-elles securisees ?
Non. Elles peuvent etre vues avec docker inspect et apparaissent dans /proc/environ. Utilisez Docker secrets pour les donnees sensibles.
Quel est l'ordre de priorite des variables d'environnement ?
Du plus eleve au plus bas : 1) docker compose run -e, 2) Variables shell hote, 3) Directive environment, 4) Directive env_file, 5) Dockerfile ENV.
Comment utiliser des variables differentes pour dev et production ?
Utilisez des fichiers override : docker-compose.yml de base + docker-compose.override.yml pour dev + docker-compose.prod.yml pour la production.
Quelle difference entre Docker secrets et variables d'environnement ?
Les secrets sont montes comme fichiers dans /run/secrets/ plutot que comme variables d'environnement. Ils sont plus securises et supportent le suffixe _FILE.