Configuration Spring Boot multi-environnements : Dev, Test, Prod
Ton application fonctionne parfaitement en local. Mais quand tu la déploies en production, catastrophe : elle se connecte à la mauvaise base de données, les logs sont trop verbeux, les ports ne correspondent pas...
Contexte technique
Versions utilisées dans cet article :
- Spring Boot 3.2+
- Java 17+
Migration Spring Boot 2.x → 3.x : Le système de profils est identique. Seules les dépendances changent (javax.* → jakarta.*).
Pourquoi c'est critique en entreprise ?
Sans configuration multi-environnements, tu exposes ton projet à trois risques majeurs :
- Incident de production : Connexion accidentelle à la base de production depuis un environnement de dev. Les données peuvent être corrompues ou supprimées.
- Coût infrastructure : Logs DEBUG en production génèrent un volume excessif. Résultat : disque saturé, puis crash applicatif.
- Faille de sécurité : Credentials en dur dans le code. Si le repo devient public (erreur de manipulation), vos accès sont exposés.
Cas terrain : Une startup a perdu l'équivalent de plusieurs mois de travail en quelques heures parce que leur application de dev, configurée avec ddl-auto: create-drop, s'est connectée à la production.
Le setup d'une configuration multi-environnements prend quelques heures. Les conséquences d'une mauvaise gestion peuvent coûter bien plus cher en temps, en argent et en confiance clients.
Dans cet article, nous allons voir comment Spring Boot te permet de gérer facilement différentes configurations pour le développement, les tests et la production, sans dupliquer de code et sans compromettre la sécurité.
Sommaire
- Pourquoi des configurations différentes
- Les profils Spring Boot
- Fichiers de configuration : application.yml vs application.properties
- Configurer chaque environnement
- Activer un profil
- Gestion des secrets : ne jamais commiter de mots de passe
- Profils conditionnels avec @Profile
- Bonnes pratiques et erreurs à éviter
- Pour aller plus loin
Pourquoi des configurations différentes
Imaginons que tu développes une application de e-commerce. Tu as besoin de configurations différentes selon l'environnement :
| Configuration | Développement | Test | Production |
|---|---|---|---|
| Base de données | H2 en mémoire | PostgreSQL locale | PostgreSQL AWS RDS |
| Logs | DEBUG (tout voir) | INFO | WARN (seulement les erreurs) |
| Console (fake) | MailHog (fake SMTP) | SendGrid (vrai) | |
| Cache | Désactivé | Désactivé | Redis activé |
| Port | 8080 | 8081 | 80 ou 443 |
Si vous utilisez la même configuration partout, vous allez :
- Envoyer de vrais emails à vos clients pendant vos tests 😱
- Avoir des logs DEBUG en production qui ralentissent l'application
- Perdre vos données de test à chaque redémarrage (si vous utilisez H2 en prod)
Spring Boot résout ça avec les profils.
Les profils Spring Boot
Un profil est un ensemble de configurations spécifique à un environnement.
Spring Boot supporte les profils nativement. Vous pouvez définir autant de profils que vous voulez :
dev: pour le développement en localtest: pour les tests automatisésstaging: pour un environnement de pré-productionprod: pour la production
Comment ça marche ?
Spring Boot charge toujours le fichier application.yml (ou .properties).
Ensuite, selon le profil actif, il charge en plus le fichier application-{profil}.yml.
Exemple :
- Si vous activez le profil
dev, Spring charge :application.yml+application-dev.yml - Si vous activez le profil
prod, Spring charge :application.yml+application-prod.yml
Les propriétés dans application-{profil}.yml remplacent celles de application.yml.
Fichiers de configuration : application.yml vs application.properties
Spring Boot supporte deux formats :
1. application.properties
spring.application.name=springcraft-app
server.port=8080
spring.datasource.url=jdbc:postgresql://localhost:5432/mydb
spring.datasource.username=admin
spring.datasource.password=secret2. application.yml (recommandé)
spring:
application:
name: springcraft-app
server:
port: 8080
spring:
datasource:
url: jdbc:postgresql://localhost:5432/mydb
username: admin
password: secretConfigurer chaque environnement
Voici une structure typique pour un projet multi-environnements :
src/main/resources/
├─ application.yml # Configuration commune à tous les profils
├─ application-dev.yml # Configuration développement
├─ application-test.yml # Configuration tests
└─ application-prod.yml # Configuration production
application.yml (configuration commune)
Ce fichier contient les propriétés communes à tous les environnements.
spring:
application:
name: springcraft-app
# Profil par défaut si aucun n'est spécifié
profiles:
active: ${SPRING_PROFILE:dev}
# Configuration commune
server:
port: 8080
logging:
pattern:
console: "%d{yyyy-MM-dd HH:mm:ss} - %msg%n"Explication :
spring.profiles.active: ${SPRING_PROFILE:dev}: Active le profildevpar défaut, sauf si la variable d'environnementSPRING_PROFILEest définie.
application-dev.yml (développement)
Configuration pour travailler en local. On veut :
- Une base de données en mémoire (H2) pour ne pas installer PostgreSQL
- Des logs détaillés (DEBUG)
- La console H2 activée pour voir la base
spring:
datasource:
url: jdbc:h2:mem:testdb
driver-class-name: org.h2.Driver
username: sa
password:
h2:
console:
enabled: true
path: /h2-console
jpa:
show-sql: true # Affiche les requêtes SQL dans la console
hibernate:
ddl-auto: create-drop # Recrée les tables à chaque démarrage
logging:
level:
com.springcraft: DEBUG # Logs détaillés pour votre code
org.hibernate.SQL: DEBUG # Logs SQLPourquoi ddl-auto: create-drop ?
En développement, on veut souvent repartir de zéro à chaque démarrage. Spring recrée automatiquement les tables.
Danger ⚠️ : Ne JAMAIS utiliser create-drop en production, sinon vous perdez toutes vos données !
application-test.yml (tests automatisés)
Configuration pour les tests unitaires et d'intégration.
spring:
datasource:
url: jdbc:h2:mem:testdb
driver-class-name: org.h2.Driver
jpa:
show-sql: false # Pas besoin de voir les SQL dans les tests
hibernate:
ddl-auto: create-drop
logging:
level:
com.springcraft: INFO # Moins verbeux pour ne pas polluer les logs de testapplication-prod.yml (production)
Configuration pour la production. On veut :
- Une vraie base de données PostgreSQL
- Pas de logs DEBUG (ralentit l'application)
- Pas de
ddl-auto(on gère les migrations avec Flyway/Liquibase) - Des connexions à la base optimisées
spring:
datasource:
url: ${DATABASE_URL} # Injecté depuis l'environnement
username: ${DATABASE_USER}
password: ${DATABASE_PASSWORD}
hikari:
maximum-pool-size: 20 # Pool de connexions optimisé
minimum-idle: 5
connection-timeout: 30000
jpa:
show-sql: false
hibernate:
ddl-auto: none # ⚠️ Ne JAMAIS modifier la structure en production
logging:
level:
com.springcraft: INFO # Seulement les infos importantes
org.springframework: WARN # Seulement les warnings/erreursNotez bien : Les informations sensibles (URL de base, user, password) sont injectées depuis l'environnement avec ${DATABASE_URL}. On ne met jamais de vrais mots de passe dans le code !
Activer un profil
Il existe plusieurs façons d'activer un profil :
1. Dans application.yml (par défaut)
spring:
profiles:
active: dev2. En ligne de commande
java -jar app.jar --spring.profiles.active=prod3. Variable d'environnement
export SPRING_PROFILES_ACTIVE=prod
java -jar app.jar4. Dans IntelliJ IDEA
- Run > Edit Configurations
- Active profiles :
dev
5. Dans les tests
@SpringBootTest
@ActiveProfiles("test") // Force le profil "test"
class UserServiceTest {
// ...
}Vérifier le profil actif
Au démarrage, Spring affiche le profil actif dans les logs :
The following 1 profile is active: "dev"
Gestion des secrets : ne jamais commiter de mots de passe
Règle d'or : Ne JAMAIS mettre de secrets (mots de passe, clés API, tokens) directement dans application.yml.
❌ Ce qu'il NE FAUT PAS faire
# application-prod.yml
spring:
datasource:
password: SuperMotDePasseSecret123 # 🚨 DANGER !Pourquoi c'est dangereux ?
- Ce fichier est versionné dans Git
- N'importe qui ayant accès au repo peut voir le mot de passe
- Si votre repo est public, c'est une faille de sécurité majeure
✅ La bonne pratique : variables d'environnement
# application-prod.yml
spring:
datasource:
url: ${DATABASE_URL}
username: ${DATABASE_USER}
password: ${DATABASE_PASSWORD}Puis, sur votre serveur de production :
export DATABASE_URL=jdbc:postgresql://prod-db.aws.com:5432/mydb
export DATABASE_USER=admin
export DATABASE_PASSWORD=SuperMotDePasseSecret123Avantages :
- Les secrets ne sont jamais dans Git
- Vous pouvez changer le mot de passe sans redéployer l'application
- Chaque environnement a ses propres secrets
Valeurs par défaut
Vous pouvez définir une valeur par défaut si la variable d'environnement n'existe pas :
spring:
datasource:
url: ${DATABASE_URL:jdbc:postgresql://localhost:5432/mydb}Signification : Si DATABASE_URL n'est pas défini, utilise jdbc:postgresql://localhost:5432/mydb.
Outils pour gérer les secrets
En production, utilisez des outils dédiés :
- AWS Secrets Manager (si vous êtes sur AWS)
- Azure Key Vault (si vous êtes sur Azure)
- HashiCorp Vault (solution agnostique)
- Kubernetes Secrets (si vous êtes sur Kubernetes)
Profils conditionnels avec @Profile
Vous pouvez activer des beans uniquement dans certains profils.
Exemple : Service d'envoi d'email
En développement, vous ne voulez pas envoyer de vrais emails. Vous voulez juste les afficher dans la console.
Interface commune :
public interface EmailService {
void sendEmail(String to, String subject, String body);
}Implémentation pour le développement :
@Service
@Profile("dev") // Activé seulement en profil "dev"
public class ConsoleEmailService implements EmailService {
@Override
public void sendEmail(String to, String subject, String body) {
System.out.println("===== EMAIL =====");
System.out.println("To: " + to);
System.out.println("Subject: " + subject);
System.out.println("Body: " + body);
System.out.println("=================");
}
}Implémentation pour la production :
@Service
@Profile("prod") // Activé seulement en profil "prod"
public class SmtpEmailService implements EmailService {
@Autowired
private JavaMailSender mailSender;
@Override
public void sendEmail(String to, String subject, String body) {
SimpleMailMessage message = new SimpleMailMessage();
message.setTo(to);
message.setSubject(subject);
message.setText(body);
mailSender.send(message); // Envoie vraiment l'email
}
}Utilisation :
@Service
public class UserService {
@Autowired
private EmailService emailService; // Spring injecte la bonne implémentation selon le profil
public void registerUser(User user) {
// ...
emailService.sendEmail(user.getEmail(), "Bienvenue !", "Merci de vous être inscrit.");
}
}En développement (dev), ConsoleEmailService est utilisé → l'email s'affiche dans la console.
En production (prod), SmtpEmailService est utilisé → l'email est vraiment envoyé.
Combiner plusieurs profils
Vous pouvez activer un bean pour plusieurs profils :
@Service
@Profile({"dev", "test"}) // Activé en dev ET en test
public class ConsoleEmailService implements EmailService {
// ...
}Ou exclure un profil :
@Service
@Profile("!prod") // Activé partout SAUF en prod
public class ConsoleEmailService implements EmailService {
// ...
}Bonnes pratiques et erreurs à éviter
✅ Bonnes pratiques
1. Toujours avoir un profil par défaut
Définissez un profil par défaut pour éviter les surprises :
spring:
profiles:
active: ${SPRING_PROFILE:dev}2. Séparer les secrets des configurations
Ne mettez jamais de secrets dans les fichiers de configuration versionnés.
3. Documenter vos profils
Créez un fichier README.md qui explique :
- Quels profils existent
- Comment les activer
- Quelles sont les différences entre chaque profil
4. Tester chaque profil
Avant de déployer en production, testez que le profil prod fonctionne correctement.
5. Utiliser des noms de profils standards
Utilisez des noms clairs et cohérents :
dev(pasdevelopment,local,debug, etc.)teststaging(si vous avez un environnement de pré-prod)prod
❌ Erreurs à éviter
1. Dupliquer toute la configuration dans chaque profil
Mauvais :
# application-dev.yml
spring:
application:
name: springcraft-app # Dupliqué partout
server:
port: 8080 # Dupliqué partout
# ...Bon :
# application.yml (configuration commune)
spring:
application:
name: springcraft-app
server:
port: 8080
# application-dev.yml (seulement ce qui change)
spring:
datasource:
url: jdbc:h2:mem:testdbImpact maintenance :
- Duplication = risque d'incohérence entre environnements
- Modification = autant de fichiers à modifier que de profils
- Sur un projet de taille réelle, cela représente un effort significativement plus important pour chaque évolution
2. Utiliser create-drop en production
# application-prod.yml
spring:
jpa:
hibernate:
ddl-auto: create-drop # 🚨 DANGER ! Supprime toutes les données au redémarrageConséquence réelle :
- Redémarrage applicatif (mise à jour, crash, etc.) = TOUTES vos données sont perdues
- Pas de retour en arrière possible sans backup
- Downtime prolongé le temps de restaurer la base
Cas réel : Une entreprise a perdu 6 mois de données clients car un développeur a oublié de retirer create-drop avant le déploiement.
En production, utilisez toujours :
spring:
jpa:
hibernate:
ddl-auto: none # ⚠️ Ne jamais laisser Hibernate modifier le schémaGérez les migrations avec Flyway ou Liquibase (contrôle de version du schéma).
Trade-off :
- Avantage ddl-auto en dev : Rapidité, pas besoin de gérer les migrations
- Inconvénient en prod : Risque énorme de perte de données
- Solution : Flyway/Liquibase en prod, ddl-auto en dev uniquement
3. Oublier de changer le profil en production
Si vous déployez en production sans activer le profil prod, vous risquez d'utiliser la configuration de développement (H2, logs DEBUG, etc.).
Impact performance :
- Logs DEBUG en production = 10x plus de logs = disque plein en quelques heures
- Disque plein = app crash = downtime
Impact sécurité :
- Console H2 exposée sur
/h2-console= accès direct à votre base de données - Logs verbeux = exposition d'informations sensibles
Solution : Automatisez l'activation du profil dans votre pipeline CI/CD.
Exemple Dockerfile :
ENV SPRING_PROFILES_ACTIVE=prodExemple Kubernetes :
env:
- name: SPRING_PROFILES_ACTIVE
value: prod4. Mélanger plusieurs profils
Évitez d'activer plusieurs profils en même temps si ce n'est pas nécessaire :
# Confus
java -jar app.jar --spring.profiles.active=dev,prodSpring va charger application-dev.yml ET application-prod.yml, ce qui peut créer des conflits.
Quand c'est utile : Profils orthogonaux (ex: prod,mysql vs prod,postgresql).
Règle : Un profil principal (dev/test/prod) + éventuellement des profils secondaires (cache, messaging).
5. Commiter des secrets dans Git
Erreur #1 en sécurité :
# application-prod.yml (dans Git)
spring:
datasource:
password: SuperMotDePasseSecret123 # 🚨 DANGER CRITIQUEConséquences :
- Secrets visibles dans l'historique Git (même si vous supprimez le commit après)
- Repo public par accident = credentials exposés sur Google
- Attaque automatisée par bots qui scannent GitHub
Cas réel :
- 2024 : Des milliers de credentials AWS exposés sur GitHub public
- Impact : factures cloud imprévues + incident de sécurité majeur
Solution :
# application-prod.yml
spring:
datasource:
password: ${DB_PASSWORD} # Variable d'environnementPuis sur le serveur :
export DB_PASSWORD=SuperMotDePasseSecret123Mieux encore : Utilisez un gestionnaire de secrets (AWS Secrets Manager, Vault, etc.).
6. Utiliser les mêmes credentials partout
Mauvais :
# application.yml
spring:
datasource:
username: admin
password: ${DB_PASSWORD} # Même mot de passe dev/test/prodPourquoi c'est dangereux :
- Si votre laptop est compromis → accès à la prod
- Si un développeur part → changer le mot de passe partout
Bon :
- Credentials différents par environnement
- Rotation régulière des mots de passe prod (tous les 90 jours)
- Principe du moindre privilège : user dev avec SELECT uniquement
7. Ne pas documenter les profils
Impact onboarding : Sans documentation, chaque nouveau développeur doit comprendre par lui-même quelle configuration utiliser. Cela ralentit l'intégration et multiplie les questions.
Solution : Créez un README.md :
## Profils disponibles
- `dev` : Développement local (H2, logs DEBUG)
- Activer : `--spring.profiles.active=dev`
- `test` : Tests automatisés (H2, logs INFO)
- Activé automatiquement dans les tests
- `prod` : Production (PostgreSQL, logs WARN)
- Variables requises : DB_URL, DB_USER, DB_PASSWORDAnti-patterns de production : cas réels
Cas 1 : L'environnement de staging qui coûte cher
Situation : Un environnement de staging configuré comme la prod, mais utilisé une fois par mois.
Erreur :
# application-staging.yml
spring:
datasource:
hikari:
maximum-pool-size: 50 # Même config que prodConséquence :
- Instance RDS dimensionnée pour la production
- Utilisée très rarement = gaspillage de ressources
Solution : Profil staging adapté
# application-staging.yml
spring:
datasource:
hikari:
maximum-pool-size: 5 # Suffisant pour les tests manuelsImpact : Réduction significative du coût d'infrastructure sans compromettre la capacité de validation.
Cas 2 : Les logs qui saturent le disque
Situation : Logs DEBUG oubliés en production.
Impact observé :
- Volume de logs excessif (plusieurs dizaines de Go par jour)
- Saturation du disque en quelques jours
- Crash applicatif quand le disque est plein
- Downtime prolongé le temps d'identifier la cause et de nettoyer
Solution :
# application-prod.yml
logging:
level:
root: WARN # Seulement les warnings et erreurs
com.springcraft: INFO # Votre code en INFORègle : En prod, jamais de DEBUG sauf sur une classe spécifique pour du troubleshooting temporaire.
Cas 3 : Le cache désactivé qui ralentit tout
Situation : Cache désactivé en prod car oublié dans la config.
Conséquences observées :
- Charge importante sur la base de données
- Latence dégradée pour les utilisateurs
- Nécessité de sur-dimensionner l'infrastructure base de données
Avec cache Redis :
- Réduction drastique des requêtes vers la base (taux de hit élevé)
- Latence divisée par 10
- Possibilité de réduire la taille de l'instance de base de données
Impact : Amélioration significative des performances et réduction du coût d'infrastructure.
Configuration :
# application-prod.yml
spring:
cache:
type: redis
data:
redis:
host: ${REDIS_HOST}
port: 6379Trade-offs : quand utiliser une autre approche
Alternative 1 : Spring Cloud Config Server
Notre approche : Fichiers YAML locaux dans src/main/resources.
Spring Cloud Config : Configuration centralisée dans un repo Git séparé.
Trade-off :
| Critère | Fichiers locaux | Spring Cloud Config |
|---|---|---|
| Complexité | Faible | Moyenne (serveur additionnel) |
| Scalabilité | Bonne (< 10 microservices) | Excellente (> 10 microservices) |
| Déploiement | Simple | Nécessite un serveur Config |
| Rafraîchissement | Redémarrage requis | Dynamique avec @RefreshScope |
| Coût infra | Minimal | Serveur Config additionnel |
Quand utiliser Spring Cloud Config :
- Vous avez 10+ microservices
- Vous voulez modifier la config sans redéployer
- Vous avez besoin d'un audit centralisé des modifications
Quand rester sur des fichiers locaux :
- Monolithe ou < 5 microservices
- Redéploiement acceptable (< 5 min)
- Budget serré
Alternative 2 : Variables d'environnement pures (12-factor app)
Notre approche : YAML + variables d'environnement pour les secrets.
12-factor pure : Tout en variables d'environnement, pas de fichiers de config.
# 12-factor pur
export SERVER_PORT=8080
export SPRING_DATASOURCE_URL=jdbc:postgresql://...
export SPRING_DATASOURCE_USERNAME=admin
export SPRING_DATASOURCE_PASSWORD=secret
# ... 50+ variablesTrade-off :
- Avantage : Configuration 100% externalisée, immuabilité des images Docker
- Inconvénient : Gestion de 50+ variables d'environnement, difficile à debugger
Quand l'utiliser :
- Environnement Kubernetes natif
- Forte contrainte de sécurité (pas de fichiers de config)
- Tooling pour gérer les variables (Helm charts, Kustomize)
Notre recommandation : Hybride (YAML pour structure, env vars pour secrets).
Alternative 3 : Profils multiples actifs
Exemple : Activer plusieurs profils simultanément.
java -jar app.jar --spring.profiles.active=prod,cache,messagingQuand c'est utile :
- Profils orthogonaux :
prod(environnement) +redis(type de cache) +kafka(messaging) - Permet de composer la config
Attention : Risque de conflits si deux profils définissent la même propriété.
Ordre de priorité : Le dernier profil listé gagne.
Pour aller plus loin
Vous savez maintenant gérer vos configurations multi-environnements ! Voici les prochaines étapes :
📚 Série d'articles complémentaires
- Comment structurer un projet Spring Boot - Organisez votre code proprement
- Tests dans Spring Boot : Guide complet - Testez vos différents profils
- Gestion des exceptions dans Spring Boot - Gérez les erreurs selon l'environnement
- CRUD complet avec Spring Boot - Un exemple complet avec profils
- Spring Boot en production : Checklist - Préparez votre déploiement
🎯 Points clés à retenir
- Utilisez les profils Spring Boot :
dev,test,prod - Ne dupliquez pas : mettez la config commune dans
application.yml - Jamais de secrets dans Git : utilisez des variables d'environnement
- Testez vos profils avant de déployer
- Profil par défaut : toujours en définir un
Avec une bonne gestion des configurations, vous éviterez 90% des problèmes de déploiement. C'est un investissement qui vaut le coup ! 🚀
Bonne configuration ! ⚙️