Spring Profiles
Spring profiles let a single build artifact behave differently per environment. You group beans and configuration files under named profiles — dev, test, prod — and activate the right set at runtime. The same JAR runs locally against an in-memory database and in production against PostgreSQL, with no code changes.
Profile-specific configuration files
Spring Boot automatically loads application-<profile>.yml (or .properties) on top of the base application.yml. The base file holds shared defaults; profile files override only what differs.
# application.yml — shared defaults
server:
port: 8080
app:
name: Devcraftly Shop
# application-dev.yml
spring:
datasource:
url: jdbc:h2:mem:devdb
logging:
level:
org.hibernate.SQL: DEBUG
# application-prod.yml
server:
port: 80
spring:
datasource:
url: jdbc:postgresql://db:5432/shop
logging:
level:
root: WARN
When prod is active, Spring merges application.yml with application-prod.yml, and the profile values win.
Activating profiles
Set spring.profiles.active through any property source. Command-line and environment variables are the usual choices for deployment.
# Command-line argument
java -jar app.jar --spring.profiles.active=prod
# Environment variable (ideal for containers)
export SPRING_PROFILES_ACTIVE=prod
java -jar app.jar
# Multiple profiles, comma-separated
java -jar app.jar --spring.profiles.active=prod,metrics
For tests, activate profiles with @ActiveProfiles:
@SpringBootTest
@ActiveProfiles("test")
class OrderServiceTest { }
Warning: Avoid setting
spring.profiles.activeinsideapplication.ymlitself — it is confusing and can be silently overridden. Activate profiles externally, per environment.
@Profile on beans
The @Profile annotation restricts a bean (or @Configuration class) to specific profiles. The bean is created only when one of the listed profiles is active.
@Configuration
public class DataConfig {
@Bean
@Profile("dev")
public CommandLineRunner seedData(ProductRepository repo) {
return args -> repo.save(new Product("Demo", BigDecimal.TEN));
}
@Bean
@Profile("prod")
public CacheManager productionCache() {
return new RedisCacheManager(/* ... */);
}
}
@Profile supports expressions:
@Profile("!prod") // any profile except prod
@Profile("dev | test") // dev OR test
@Profile({"prod", "staging"}) // prod OR staging
Annotating an entire configuration class gates all of its beans at once:
@Configuration
@Profile("cloud")
public class CloudConfig {
// every @Bean here is cloud-only
}
Profile groups
A profile group activates several profiles under one name, so operators set a single value. Define groups in the base configuration.
# application.yml
spring:
profiles:
group:
production:
- prod
- metrics
- audit
local:
- dev
- embedded-db
Now activating production turns on prod, metrics, and audit together:
java -jar app.jar --spring.profiles.active=production
This keeps deployment commands simple while letting you split configuration into focused, reusable profiles.
The default profile
When no profile is active, Spring uses the default profile. Beans marked @Profile("default") and an application-default.yml file apply only when nothing else is selected.
@Bean
@Profile("default")
public DataSource embeddedDataSource() {
return new EmbeddedDatabaseBuilder().build();
}
You can rename the fallback with spring.profiles.default, though this is rarely needed.
Note: The
defaultprofile is also active alongside named profiles only when no profile is explicitly set. Once you activatedev,defaultno longer applies — plan your defaults in the baseapplication.ymlinstead.
Inspecting active profiles
Check which profiles are active at runtime via the Environment:
@Component
public class ProfileLogger {
public ProfileLogger(Environment env) {
System.out.println("Active: " + Arrays.toString(env.getActiveProfiles()));
}
}
Output:
Active: [prod, metrics, audit]
Profiles comparison
| Mechanism | Scope | Use it for |
|---|---|---|
application-<profile>.yml | Property values | Environment-specific settings |
@Profile on a bean | Bean registration | Swapping implementations per environment |
| Profile group | Activation convenience | Bundling profiles under one name |
default profile | Fallback | Sensible behavior when nothing is set |
Best Practices
- Keep one base
application.ymlwith shared defaults; put only differences in profile files. - Activate profiles externally (env var or command-line), never hard-code them in YAML.
- Use
@Profileto swap beans like data sources, caches, and seed-data runners per environment. - Bundle related profiles with profile groups so deployments set a single profile name.
- Do not rely on the
defaultprofile in production — activate an explicit profile every time. - Verify the active profiles on startup so misconfiguration is obvious in the logs.