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 test task. It does not affect bootRun or 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=dev is hardcoded in application.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.yml but started the app with --spring.profiles.active=production
    → The filename and Profile name must match exactly.

When in doubt, this structure is the most manageable:

  • application.yml: shared configuration
  • application-dev.yml: development environment overrides
  • application-prod.yml: production environment overrides
  • application-test.yml: test environment overrides
  • Always explicitly set SPRING_PROFILES_ACTIVE at 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:

  1. Startup argument (--spring.profiles.active=...)
  2. Environment variable (SPRING_PROFILES_ACTIVE)
  3. application-<profile>.yml
  4. application.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.active in application.yml
  • Record “which Profile was used for verification” in pull requests
  • When adding a new configuration key, always check whether a dev/prod/test difference 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>.yml to separate configuration values
  • Activate profiles via --spring.profiles.active or SPRING_PROFILES_ACTIVE
  • Use @Profile to swap Beans per environment
  • Pass spring.profiles.active=test to Gradle’s test task to lock in a Profile during test execution
  • Always explicitly specify prod in production to prevent accidents

Put Profiles to work and achieve safe, clean environment separation!