What Is a Profile?
A Profile in Spring Boot is a switch that indicates “which environment is currently active (development, staging, production, etc.).”
Depending on this switch, you can toggle Bean activation and configuration values (database connection targets, log levels, external API URLs, etc.).
For example, you can safely handle the following kinds of switching:
- H2 in development, PostgreSQL in production
- Verbose logging in development, INFO-level logging in production
- Mock external APIs in development, real APIs in production
The major value of Profiles is reducing accidents like “manually rewriting config and deploying.”
What Does a Profile Switch Control?
There are two main targets that Profiles control:
- Values in configuration files (
application.yml/.properties) - Activation of Spring Bean definitions (
@Bean/@Component, etc.)
The Basics of Splitting Configuration Files by Profile
The most common approach is splitting configuration using application-<profile>.yml (or .properties).
For example, you can split files like this:
application.yml(shared)application-dev.yml(development)application-prod.yml(production)application-test.yml(testing)
application.yml (shared)
spring:
application:
name: demo
logging:
level:
root: INFO
application-dev.yml (development)
spring:
datasource:
url: jdbc:h2:mem:testdb
jpa:
hibernate:
ddl-auto: create-drop
logging:
level:
root: DEBUG
application-prod.yml (production)
spring:
datasource:
url: jdbc:postgresql://db.prod.example.com:5432/app
jpa:
hibernate:
ddl-auto: validate
logging:
level:
root: INFO
application-test.yml (testing)
spring:
datasource:
url: jdbc:h2:mem:testdb
jpa:
hibernate:
ddl-auto: create-drop
logging:
level:
root: WARN
The key point is: “put shared settings in application.yml, and write only environment-specific differences in the profile files.”
This makes differences easier to spot and reduces configuration duplication.
How to Activate a Profile
There are several ways to activate (select) a Profile. Here they are in order of common usage.
Specify via startup argument
Convenient for local execution or temporary switching.
java -jar app.jar --spring.profiles.active=dev
Specify via environment variable
Commonly used in container-based operations such as Docker or Kubernetes.
export SPRING_PROFILES_ACTIVE=prod
Specify via application.yml (use with caution)
spring:
profiles:
active: dev
Be careful with this approach, as it tends to lock the app into always running with dev.
If CI/CD or production deployments are involved, it is safer to control activation via environment variables or deployment configuration.
Always Enabling the test Profile When Running Tests with Gradle
If you want the test Profile to be active every time you run ./gradlew test, the easiest approach is to pass it as a system property to Gradle’s test task.
Groovy DSL (build.gradle)
tasks.named('test') {
useJUnitPlatform()
systemProperty 'spring.profiles.active', 'test'
}
With this, the test Profile will always be active whenever tests are run via Gradle.
If you prefer to pass it as an environment variable, you can write it like this instead:
tasks.named('test') {
useJUnitPlatform()
environment 'SPRING_PROFILES_ACTIVE', 'test'
}
Kotlin DSL (build.gradle.kts)
tasks.test {
useJUnitPlatform()
systemProperty("spring.profiles.active", "test")
}
Notes
- This setting only applies to Gradle’s
testtask. It does not affectbootRunor other tasks. - If you run JUnit directly from your IDE (without using Gradle’s run configuration), this setting may not take effect. In that case, consider switching your IDE to run tests via Gradle, or add
@ActiveProfiles("test")to your test classes.
Switching Beans by Profile
Not just configuration values — you can also swap out Beans themselves using Profiles.
Switching at the class level with @Profile
import org.springframework.context.annotation.Profile;
import org.springframework.stereotype.Component;
@Profile("dev")
@Component
public class DevOnlyInitializer {
}
This Bean is only registered when the dev Profile is active.
Also works with @Configuration + @Bean
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Profile;
@Configuration
public class ClientConfig {
@Profile("dev")
@Bean
public ApiClient mockClient() {
return new ApiClient("http://localhost:8081");
}
@Profile("prod")
@Bean
public ApiClient realClient() {
return new ApiClient("https://api.example.com");
}
}
This lets you swap out an external API client depending on the environment.
Activating More Than One Profile
Multiple Profiles can be active at the same time.
--spring.profiles.active=dev,feature-x
In this case, both dev and feature-x are active.
Designing your Profiles to separate “environment” from “feature flags” makes switching smoother.
Grouping Profiles with group
As the number of Profiles grows, you may want something like: “when I select dev, automatically enable the dev-specific and local-specific profiles too.”
That’s where Profile Groups come in handy.
spring:
profiles:
group:
dev:
- dev
- local
With this, setting spring.profiles.active=dev will activate both dev and local together.
Common Pitfalls and How to Avoid Them
Starting production with the dev configuration
The cause is usually one of the following:
spring.profiles.active=devis hardcoded inapplication.yml- The environment variable is not set in the deployment environment
- The startup script is outdated
The recommended fix: always explicitly set SPRING_PROFILES_ACTIVE=prod in your production deployment configuration.
Losing track of configuration priority
When the same key exists in multiple places, it gets confusing. The basic mental model is:
- “More external” (environment variables, startup arguments) wins
- “More specific” (profile-specific files) often wins over the shared file
If you’re stuck, check the startup logs — they show which Profiles are active, which usually helps you resolve issues quickly.
application-xxx.yml is not being loaded
Typos in the Profile name are a common culprit.
- You created
application-prod.ymlbut started the app with--spring.profiles.active=production
→ The filename and Profile name must match exactly.
Recommended Setup for Development and Operations
When in doubt, this structure is the most manageable:
application.yml: shared configurationapplication-dev.yml: development environment overridesapplication-prod.yml: production environment overridesapplication-test.yml: test environment overrides- Always explicitly set
SPRING_PROFILES_ACTIVEat startup (especially in production)
Values that should only exist in production (passwords, API keys, etc.) should not be hardcoded in configuration files — pass them via environment variables or a secrets manager (such as Kubernetes Secrets) for safety.
Keeping Priority Order Straight
When the same key is written in multiple places, it becomes easy to lose track of which value is actually used.
In practice, memorizing the following priority order (strongest first) will speed up troubleshooting:
- Startup argument (
--spring.profiles.active=...) - Environment variable (
SPRING_PROFILES_ACTIVE) application-<profile>.ymlapplication.yml
As an operational policy, deciding that “production always uses environment variables; config files focus only on defining differences” will minimize accidents.
Team Development Guidelines
When Profile discipline breaks down, environment-specific bugs occur frequently. The following rules are recommended:
- Do not hardcode
spring.profiles.activeinapplication.yml - Record “which Profile was used for verification” in pull requests
- When adding a new configuration key, always check whether a
dev/prod/testdifference is needed - Do not store secrets in the repository — centralize them in environment-side secrets management
Profiles are powerful, but they only work safely when paired with clear operational guidelines.
Summary
Spring Boot Profiles let you safely switch configuration and Beans per environment.
- Use
application-<profile>.ymlto separate configuration values - Activate profiles via
--spring.profiles.activeorSPRING_PROFILES_ACTIVE - Use
@Profileto swap Beans per environment - Pass
spring.profiles.active=testto Gradle’stesttask to lock in a Profile during test execution - Always explicitly specify
prodin production to prevent accidents
Put Profiles to work and achieve safe, clean environment separation!