Skip to content
Spring Boot sb config 3 min read

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.active inside application.yml itself — 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 default profile is also active alongside named profiles only when no profile is explicitly set. Once you activate dev, default no longer applies — plan your defaults in the base application.yml instead.

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

MechanismScopeUse it for
application-<profile>.ymlProperty valuesEnvironment-specific settings
@Profile on a beanBean registrationSwapping implementations per environment
Profile groupActivation convenienceBundling profiles under one name
default profileFallbackSensible behavior when nothing is set

Best Practices

  • Keep one base application.yml with shared defaults; put only differences in profile files.
  • Activate profiles externally (env var or command-line), never hard-code them in YAML.
  • Use @Profile to 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 default profile in production — activate an explicit profile every time.
  • Verify the active profiles on startup so misconfiguration is obvious in the logs.
Last updated June 13, 2026
Was this helpful?