Spring Boot Profiles
Different environments need different configurations — in-memory H2 for dev, PostgreSQL for production, debug logging for dev, minimal logging for prod. Profiles solve this cleanly.
Profile-Specific Configuration Files
src/main/resources/
├── application.yml ← Shared config (all profiles)
├── application-dev.yml ← Dev overrides
├── application-staging.yml ← Staging overrides
└── application-prod.yml ← Production overrides
application.yml (shared)
app:
name: Bookstore API
version: 1.0.0
spring:
jpa:
open-in-view: false
application-dev.yml
server:
port: 8080
spring:
datasource:
url: jdbc:h2:mem:devdb
driver-class-name: org.h2.Driver
h2:
console:
enabled: true
jpa:
hibernate:
ddl-auto: create-drop
show-sql: true
logging:
level:
com.bookstore: DEBUG
org.hibernate.SQL: DEBUG
application-prod.yml
server:
port: 8080
spring:
datasource:
url: ${DATABASE_URL}
username: ${DATABASE_USERNAME}
password: ${DATABASE_PASSWORD}
jpa:
hibernate:
ddl-auto: validate
show-sql: false
logging:
level:
root: WARN
com.bookstore: INFO
Activating a Profile
# Command line
java -jar app.jar --spring.profiles.active=prod
# Environment variable
SPRING_PROFILES_ACTIVE=prod java -jar app.jar
# In application.yml (default profile)
spring:
profiles:
active: dev
@Profile Annotation — Profile-Specific Beans
@Configuration
@Profile("dev")
public class DevConfig {
@Bean
public DataLoader dataLoader(BookRepository bookRepository) {
return new DataLoader(bookRepository); // seed sample data in dev
}
}
@Configuration
@Profile("prod")
public class ProdConfig {
@Bean
public DataSource dataSource() {
// Production-grade connection pool
HikariDataSource ds = new HikariDataSource();
ds.setMaximumPoolSize(20);
ds.setMinimumIdle(5);
return ds;
}
}
@Service
@Profile("!prod") // active in all profiles EXCEPT prod
public class MockPaymentService implements PaymentService {
public void charge(BigDecimal amount) {
System.out.println("Mock charge: $" + amount);
}
}
@Service
@Profile("prod")
public class StripePaymentService implements PaymentService {
public void charge(BigDecimal amount) {
// Real Stripe integration
}
}
Default Profile
If no profile is active, Spring uses the default profile:
@Profile("default")
@Service
public class FallbackService { }
Multiple Active Profiles
java -jar app.jar --spring.profiles.active=prod,metrics,ssl
spring:
profiles:
active: dev,swagger
Real-World Setup
Environment Profile Database Logging Features
────────────── ────────── ─────────────── ───────── ────────
Local dev dev H2 in-memory DEBUG Swagger, sample data
Staging staging PostgreSQL INFO Swagger
Production prod PostgreSQL WARN No swagger, monitoring
Common Mistakes
| Mistake | Fix |
|---|---|
Hardcoding spring.profiles.active=dev |
Set via env var in production |
Secrets in application-prod.yml |
Use environment variables: ${DB_PASSWORD} |
| No profile for tests | Create application-test.yml or use @ActiveProfiles("test") |
ddl-auto=create-drop in production |
Use validate or none — use Flyway for migrations |
Key Takeaways
- Profile-specific files:
application-{profile}.ymloverrideapplication.yml. - Activate with
--spring.profiles.active=prodorSPRING_PROFILES_ACTIVE. @Profilecreates beans only for specific environments.- Use
devfor H2 + debug logging,prodfor PostgreSQL + env vars. - Never commit production secrets — use environment variables.
What’s Next?
Next: Building a REST API — your first real Spring Boot CRUD API.