Foundation · Configure

Environment Profiles

Profiles split environment-specific configuration into named sets; each deployment activates exactly one and the startup log proves which.

Primary source: Spring Boot 3.5 Reference: Externalized Configuration

15 min

Lesson 3 of 4

1d streak

The Architectural Question

One shared configuration file for every environment means a local run is one typo away from pointing at the production database.

What this guide proves

  • @Profile
  • spring.profiles.active
  • application-{profile}.yml

Spring Profiles: Environment Slicing & Activation

Spring Profiles provide a way to segregate parts of your application configuration and make it available only in certain environments. A profile can activate bean definitions (via @Profile), external configuration files (via application-{profile}.yml), or specific document sections inside a multi-document YAML.

NotificationConfig.java
@Configuration
public class NotificationConfig {

    @Bean
    @Profile("dev | test")
    public NotificationSender mockNotificationSender() {
        return new LoggingNotificationSender();
    }

    @Bean
    @Profile("prod")
    public NotificationSender sesNotificationSender(AwsProperties props) {
        return new AmazonSesNotificationSender(props.sesClient());
    }
}

Profile Expressions in Spring 5.1+ / Spring Boot 3+

You can use rich Boolean expressions in @Profile: ! (NOT), & (AND), and | (OR). For example, @Profile("cloud & !local") activates only when the cloud profile is active and the local profile is NOT active.

Active Recall Checkpoint

What happens to a bean annotated with @Profile("prod") when spring.profiles.active=dev?

Hint: Does the container register the bean definition?

Show answer & explanation

The container evaluates the @Profile condition during the bean definition registration phase. Because the condition evaluates to false, the bean definition is discarded; the bean is never instantiated and cannot be injected.

Modern Profile Activation with Multi-Document YAML & spring.config.activate

Spring Boot 2.4+ introduced a modernized configuration file processing model. Instead of maintaining dozens of separate files (application-dev.yml, application-staging.yml, application-prod.yml), you can group profile-specific configuration cleanly within a single application.yml using standard YAML document separators (---) and spring.config.activate.on-profile.

application.yml
# Common default configuration
spring:
  application:
    name: order-service
  datasource:
    hikari:
      maximum-pool-size: 10

---
# Development profile overrides
spring:
  config:
    activate:
      on-profile: dev
  jpa:
    show-sql: true
logging:
  level:
    com.example: DEBUG

---
# Production profile overrides
spring:
  config:
    activate:
      on-profile: prod
  datasource:
    hikari:
      maximum-pool-size: 50
logging:
  level:
    root: WARN
    com.example: INFO

Profile Precedence Order

Profile-specific documents always override unprofiled default documents. If spring.datasource.hikari.maximum-pool-size is 10 in the default section and 50 in the prod section, activating prod results in 50.

Active Recall Checkpoint

How can an operator activate the "prod" profile when starting the application via command line?

Hint: Specify the active profile property via Java system property or CLI argument.

Show answer & explanation

Run java -jar app.jar --spring.profiles.active=prod or pass -Dspring.profiles.active=prod. In container environments, set SPRING_PROFILES_ACTIVE=prod.

Profile Anti-Patterns: Environment Sprawl & Build-Time Secrets

Misusing Spring profiles is one of the most common sources of enterprise maintenance headaches. Senior engineers adhere to the Twelve-Factor App methodology to prevent profile anti-patterns.

Anti-PatternWhy It Fails in ProductionRecommended Modern Practice
Building different JARs per profile (e.g. Maven profiles)Violates continuous delivery: the artifact tested in staging is not the exact binary deployed to prod.Build a single immutable JAR once; configure behavior at runtime via environment variables and external config.
Hardcoding database passwords in application-prod.ymlExposes production credentials in Git history and source repositories.Use placeholder references (${DB_PASSWORD}) or Kubernetes secret mounts with configtree:.
Profile Explosion (dev-local, dev-remote, qa1, qa2, staging-eu)Becomes impossible to maintain and reason about as environments scale.Keep profiles minimal (dev, test, prod). Vary fine-grained infrastructure endpoints via environment variables.

Active Recall Checkpoint

Why is building a single immutable JAR across all environments considered a non-negotiable DevOps principle?

Hint: Consider what guarantees QA testing gives if a new artifact is compiled for production.

Show answer & explanation

If a different JAR is built for production, you are deploying untested bytecode. A single immutable JAR guarantees that what passed automated tests and staging qualification is bit-for-bit identical to what runs in production.

Deliberate Failure Lab: Missing Profile Beans & NoSuchBeanDefinitionException

A common production crash occurs when a collaborator is marked with @Profile("prod"), but no bean is provided for the default or dev profile. If a service depends on that interface, the container cannot start.

startup-nosuchbean.log
Field sender in com.example.service.OrderService required a bean of type
'com.example.notification.NotificationSender' that could not be found.

The following candidates were found but could not be injected:
  - Bean method 'sesNotificationSender' in 'NotificationConfig' not loaded because @Profile [prod] did not match active profiles [dev]

Action:
Consider defining a bean of type 'com.example.notification.NotificationSender' for the active profiles.

Remediation Pattern: Default Fallback Beans

Always provide a default bean or use @ConditionalOnMissingBean. Alternatively, mark the development bean with @Profile("!prod") or @Profile("default") so an implementation is always guaranteed to satisfy the injection point regardless of active profiles.

Active Recall Checkpoint

What profile is active if spring.profiles.active is left empty or unspecified?

Hint: What is the default profile name in Spring Boot?

Show answer & explanation

The default profile is active. You can target this fallback profile explicitly using @Profile("default").

Runtime Architecture & Flow

Resolution model

Profile composition model

  1. 1

    Load shared application defaults

  2. 2

    Activate a named environment profile

  3. 3

    Merge profile-specific overrides

  4. 4

    Apply external runtime values

  5. 5

    Inspect the effective Environment

A profile selects a configuration group; it should not become a hidden substitute for feature flags or secret management.

Synthesis & Production Verification

Production Acceptance Criteria

  • Create application-dev.yml and application-prod.yml with distinct datasources
  • Activate a profile with --spring.profiles.active and verify it in the startup log
  • Show that @Profile beans appear only under their matching profile

Architectural & Technical Review

Core Interview Questions

Q01How does Spring evaluate profile activation on beans and configuration classes?Show answer

The container evaluates @Profile expressions (supporting boolean operators !, &, |) during bean definition registration. If the condition evaluates to false, the bean definition is discarded and never instantiated.

Q02How does modern Spring Boot manage profile configuration in a single application.yml?Show answer

Using YAML document separators (---) and the spring.config.activate.on-profile directive, allowing shared defaults and profile overrides to be version-controlled cohesively in one file.

Q03Why is compiling different JAR artifacts per environment an anti-pattern?Show answer

It violates continuous delivery: the binary tested in staging is not the exact binary deployed to production. A single immutable JAR must be built once and configured at runtime via external properties.

Production Pitfalls

Critical Anti-Patterns

  • Pitfall: Building environment-specific JARs instead of using runtime configuration — Always build a single immutable artifact; inject environment differences at runtime via environment variables.
  • Pitfall: Committing production credentials to profile files — Never store production database passwords or API keys in application-prod.yml in source control.

Primary Documentation & Specifications

Supplementary Module Resources