3.4Variables, profils et surcharges par environnement
L'essentiel en 30 secondes
- Le .env : une valeur, plusieurs visages : ${PORT:-3000} lit la variable ou son défaut — un fichier .env par environnement, un seul compose pour N visages.
- Les surcharges : socle + variante : compose.override.yml auto-chargé en dev, -f compose.yml -f compose.prod.yml en prod — chaque fichier ne décrit que sa différence.
- Les profils : services opt-in : profiles: [dev] rend adminer absent par défaut, présent avec --profile dev — le stack par défaut est minimal et sûr.
Mini-cas filé — Idir unifie dev et prod
Idir, devops junior à Alger, maintient deux compose.yml copiés (dev avec adminer, prod sans) — ils divergent, et un jour la console dev part en production. Il refond : un socle unique avec ${PORT:-3000}, adminer sous profiles: [dev], .env par environnement non commité. Dev : --profile dev up -d ; prod : up -d, sobre par défaut. Une seule source de vérité, deux visages — la prod ne contient plus jamais les outils de dev.
Ce même cas revient dans le cours, le schéma, le script et le défi 48h.
1. Interpolation et .env : paramétrer sans dupliquer
Dans compose.yml, ${PORT:-3000} lit la variable PORT ou 3000 par défaut : le compose devient PARAMÉTRABLE. Un fichier .env à côté du compose fixe les valeurs par environnement (dev : ports publiés + adminer ; prod : autres valeurs, pas d'adminer) — chargé automatiquement. Un fichier, N visages : la même image, la même structure, des valeurs qui changent.
Règle d'hygiène : le .env contenant des secrets n'est JAMAIS commité — on versionne un .env.example factice qui documente les clés attendues. Et les défauts ${X:-…} sont choisis prod-sûrs : sans variable, le stack démarre dans sa forme la plus sobre, pas la plus ouverte.
2. Surcharges -f et profils : variantes et opt-in
Les SURCHARGES : on garde un compose.yml SOCLE et on ajoute des variantes — compose.override.yml, chargé automatiquement en dev (bind mounts, debug), ou compose.prod.yml appelé explicitement (-f compose.yml -f compose.prod.yml). Chaque fichier ne décrit que sa DIFFÉRENCE ; de gauche à droite, chaque -f surcharge le précédent. Fini la duplication intégrale par environnement.
Les PROFILS : profiles: [dev] sur adminer le rend OPT-IN — absent par défaut, présent avec --profile dev. Fini le « j'ai oublié d'enlever la console en prod » : par défaut, le stack est minimal et sûr, les outils dev s'activent explicitement. Socle minimal, options en profils, défaut prod-sûr.
🔎 Exemple concret — Dev riche, prod sobre : même socle, deux visages
Idir paramètre le port, met adminer en profil dev, et lance chaque environnement.
# compose.yml (socle)
services:
api:
build: ./api
ports: ["${PORT:-3000}:3000"]
networks: [appnet]
adminer:
image: adminer:4.8
profiles: [dev]
ports: ["8081:8080"]
networks: [appnet]
networks:
appnet:
# .env (dev, NON commité) : PORT=3000
# Dev : docker compose --profile dev up -d
# Prod : PORT=80 docker compose up -d
Lecture : même socle, adminer seulement en dev, port par variable.
Leçon : la prod ne contient jamais les outils de dev — sobre par défaut.
Vocabulaire de la section
- Interpolation
- ${VAR:-défaut} résolu depuis l'environnement ou le .env : le compose paramétrable.
Exemple simple : Le ${PORT:-3000} d'Idir, 3000 sans variable.
Ce que ce n'est pas : un port en dur dupliqué dans deux fichiers.
Réflexe : défaut prod-sûr après chaque :-.
- Surcharge
- Fichier -f supplémentaire qui modifie le socle : chaque -f surcharge le précédent, de gauche à droite.
Exemple simple : -f compose.yml -f compose.prod.yml en production.
Ce que ce n'est pas : un second compose copié à 95 % identique.
Réflexe : chaque variante ne décrit que sa différence.
- override
- Surcharge dev (compose.override.yml) chargée automatiquement : binds, debug, sans toucher au socle.
Exemple simple : Les montages live d'Idir en dev uniquement.
Ce que ce n'est pas : des binds de dev qui fuient en production.
Réflexe : dev dans l'override, jamais dans le socle.
- Profil
- Groupe opt-in (profiles: [dev]) activé par --profile : service absent par défaut.
Exemple simple : Adminer présent seulement avec --profile dev.
Ce que ce n'est pas : la console dev qui part en prod par oubli.
Réflexe : outils dev en profils, stack sobre par défaut.
- .env
- Fichier local de valeurs à côté du compose, chargé auto — non commité s'il contient des secrets.
Exemple simple : Le .env d'Idir avec PORT et mots de passe locaux.
Ce que ce n'est pas : un .env avec secrets poussé au dépôt.
Réflexe : versionner .env.example, jamais les secrets.
En pratique — Dev/prod sans duplication
- Ajoutez ${PORT:-3000} et un profil dev (adminer) à votre compose socle.
- Lancez sans profil : vérifiez qu'adminer est absent (stack sobre).
- Relancez avec --profile dev : adminer apparaît — opt-in explicite.
- Changez PORT via l'environnement et relancez : le port suit, sans toucher au fichier.
Script prêt à dire — justifier le socle unique (30 s)
« Un seul compose socle : le port vient d'une variable, la console dev d'un profil opt-in, les différences de la prod d'une surcharge. Dev riche avec --profile dev, prod sobre par défaut — plus aucun fichier copié qui diverge, plus aucun outil dev qui fuit en prod. »
À dire en revue d'architecture ; montrer le diff vide entre dev et prod.
Modèle copier-coller — socle paramétré + profil dev
# Socle : dev riche en opt-in, prod sobre par défaut.
services:
api:
build: ./api
ports:
- "${PORT:-3000}:3000"
environment:
DATABASE_URL: ${DATABASE_URL}
networks:
- appnet
adminer:
image: adminer:4.8
profiles:
- dev
ports:
- "8081:8080"
networks:
- appnet
networks:
appnet:
# Dev : docker compose --profile dev up -d
# Prod : docker compose -f compose.yml -f compose.prod.yml up -d
Indépendant
Socle + .env.example et chaque client décline sans toucher au fichier.
PME à Alger
Idir unifie dev et prod : un socle, deux .env, zéro divergence.
Équipe projet
Imposer socle minimal + profils opt-in + défauts prod-sûrs dans la charte.
Débutant
Cette semaine : une variable ${PORT:-3000} + un service en profil dev.
✓ Bonnes pratiques
- Gardez le socle minimal, mettez les options en profils opt-in.
- Choisissez des défauts prod-sûrs après chaque :- (${X:-…}).
- Versionnez un .env.example, jamais les secrets réels.
✗ Pièges fréquents
- Dupliquer tout le fichier par environnement : les copies divergent.
- Profils dev actifs par défaut : l'opt-in n'existe plus, la console fuit en prod.
- Committer un .env avec mots de passe : secret exposé dans l'historique.
Points clés à retenir
- ${VAR:-défaut} rend le compose paramétrable : un fichier, N visages.
- SURCHARGES pour les différences (-f, override dev) : jamais de duplication.
- PROFILS opt-in pour les outils : absent par défaut, --profile dev pour activer.
- DÉFAUT = prod sûre ; .env.example versionné, secrets jamais commités.
Défi 48h — appliquez demain
Fusionnez 2 compose copiés en 1 socle + 1 profil dev + 1 surcharge, et prouvez que la prod démarre sobre.
- Mon socle unique sert dev et prod sans copier-coller.
- Mes outils dev sont en opt-in, sobres par défaut.
- Mes secrets sont hors du dépôt, mes défauts prod-sûrs.
Questions fréquentes
Où placer le .env, et comment le garder hors du dépôt ?
À CÔTÉ du compose.yml : chargé automatiquement, un seul par environnement. S'il contient des secrets, jamais commité (ajoutez-le au .gitignore) — fournissez un .env.example versionné avec des valeurs factices documentant chaque clé. En prod, les valeurs viennent de l'environnement ou d'un secret managé.
Plusieurs -f, dans quel ordre, et override ou prod ?
De GAUCHE À DROITE : chaque -f surcharge le précédent — le socle d'abord, la variante ensuite (-f compose.yml -f compose.prod.yml). compose.override.yml est l'exception : chargé AUTOMATIQUEMENT (sans -f), il porte le dev (binds, debug). Règle : dev = override auto, prod = -f explicite, et chaque fichier ne décrit que sa différence.
Adminer ne doit exister qu'en dev. Quelle mécanique ?