Course topics

By WebNest Studio Engineering Team

Spring Boot Tutorial

Spring Boot Profiles

Real applications need different configuration for local development, automated testing, staging, and production — different database URLs, different logging levels, different feature flags. Spring Profiles let you define named sets of configuration and beans that only activate for a chosen environment, without maintaining separate codebases.

Profile-Specific Configuration Files

Alongside the default application.properties (or application.yml), you can create application-{profile}.properties files — for example application-dev.properties and application-prod.properties. Values in the profile-specific file override or add to the base file when that profile is active. In application.yml, the same effect is achieved with multi-document files separated by --- and a spring.config.activate.on-profile key.

Activating a Profile

The active profile is set with the property spring.profiles.active=dev, which can be provided in application.properties, as a JVM argument (-Dspring.profiles.active=prod), as an environment variable (SPRING_PROFILES_ACTIVE=prod), or as a command-line argument when launching the jar. Multiple profiles can be active simultaneously as a comma-separated list, and later ones override earlier ones on conflicting keys.

Profile-Specific Beans

Beyond properties, the @Profile("dev") annotation on a @Bean method or @Component class restricts that bean to only be created when the named profile is active — useful for swapping a real payment gateway client for a mock one during local development and testing.

Profile Expressions: !, & and |

@Profile accepts simple expressions. @Profile("!prod") creates the bean in every environment except production, which is the safest way to declare a fake payment gateway: a new "staging" or "demo" profile automatically gets the fake one, and only prod can ever charge a real card. @Profile("prod & eu") needs both profiles, and @Profile("dev | test") needs either.

Profile Groups

Large applications often split production settings into several files, such as one for the database and one for mail. Instead of asking everyone to remember --spring.profiles.active=prod,prod-db,prod-mail, define a group in the base file: spring.profiles.group.prod=prod-db,prod-mail. Activating prod then switches on all three, and the startup log lists every one of them.

Which Value Wins

Properties from an active profile file override the same keys in application.properties, and keys that a profile file does not mention keep their base values. When several profiles are active and set the same key, the profile listed last wins: with dev,prod you get the prod value, and with prod,dev you get the dev value. When no profile is set at all, Spring Boot uses one called default and logs No active profile set, falling back to 1 default profile: "default".

When to Use Profiles, and When Not To

  • Use them for real differences between environments: database URLs, log levels, which implementation of an interface is wired in, and whether test data is seeded.
  • Use them in tests with @ActiveProfiles("test") on a test class, so tests get their own settings without touching the developer's local configuration.
  • Do not use them for secrets. Passwords and API keys belong in environment variables or a secrets manager, which override files without being committed.
  • Do not use them as feature flags. A profile is fixed for the whole run, so a setting you want to switch per customer or at runtime should be an ordinary property or a feature-flag service.

Examples

Profile-specific datasource configuration and a profile-restricted bean

Java
// application-dev.properties
spring.datasource.url=jdbc:h2:mem:devdb
logging.level.root=DEBUG

// application-prod.properties
spring.datasource.url=jdbc:postgresql://db.internal:5432/webnest
logging.level.root=WARN

// PaymentConfig.java
@Configuration
public class PaymentConfig {

    @Bean
    @Profile("dev")
    public PaymentGateway mockPaymentGateway() {
        return new MockPaymentGateway();
    }

    @Bean
    @Profile("prod")
    public PaymentGateway stripePaymentGateway() {
        return new StripePaymentGateway();
    }
}

// Run with: java -jar app.jar --spring.profiles.active=dev
Output
... The following 1 profile is active: "dev"
... HikariPool-1 - Starting... url=jdbc:h2:mem:devdb
... Started WebnestAppApplication in 1.65 seconds

A profile group, a "!prod" bean and the override order (tested on Spring Boot 4.1.1)

Java
# application.properties
app.greeting=Hello from the base file
app.region=ap-south-1
spring.profiles.group.prod=prod-db,prod-mail

# application-dev.properties
app.greeting=Hello from dev

# application-prod.properties
app.greeting=Hello from prod

# application-prod-db.properties
app.db=managed postgres

// ProfileReport.java
@Configuration
public class ProfileReport {

    interface PaymentGateway { String name(); }

    @Bean
    @Profile("!prod")   // every environment except production
    PaymentGateway fakeGateway() { return () -> "fake gateway (no real charges)"; }

    @Bean
    @Profile("prod")
    PaymentGateway realGateway() { return () -> "real gateway"; }

    @Bean
    CommandLineRunner report(Environment env, PaymentGateway gateway,
                             @Value("${app.greeting}") String greeting,
                             @Value("${app.region}") String region,
                             @Value("${app.db:none}") String db) {
        return args -> {
            System.out.println("active=" + String.join(",", env.getActiveProfiles()));
            System.out.println("greeting=" + greeting + " | region=" + region + " | db=" + db);
            System.out.println("gateway=" + gateway.name());
        };
    }
}
Output
$ java -jar app.jar
No active profile set, falling back to 1 default profile: "default"
active=
greeting=Hello from the base file | region=ap-south-1 | db=none
gateway=fake gateway (no real charges)

$ java -jar app.jar --spring.profiles.active=prod
The following 3 profiles are active: "prod", "prod-db", "prod-mail"
active=prod,prod-db,prod-mail
greeting=Hello from prod | region=ap-south-1 | db=managed postgres
gateway=real gateway

$ java -jar app.jar --spring.profiles.active=prod,dev
The following 4 profiles are active: "prod", "prod-db", "prod-mail", "dev"
active=prod,prod-db,prod-mail,dev
greeting=Hello from dev | region=ap-south-1 | db=managed postgres
gateway=real gateway

Common Mistakes

  • Forgetting to set spring.profiles.active anywhere and being confused why "default" configuration always loads instead of the intended dev/prod file.
  • Marking the fake implementation @Profile("dev") instead of @Profile("!prod"). A new staging or demo profile then has no PaymentGateway bean at all, and startup fails.
  • Expecting the first profile in spring.profiles.active to win a conflict. The last one listed wins.
  • Duplicating every property in each profile file instead of putting shared defaults in application.properties and only the differences in the profile-specific files.
  • Hardcoding "prod" behavior directly in code with if-statements instead of using @Profile beans or profile-specific properties.
  • Committing a profile file containing real production secrets (passwords, API keys) directly into version control.

Exercise

A teammate is about to make this mistake: Forgetting to set spring.profiles.active anywhere and being confused why "default" configuration always loads instead of the intended dev/prod file. Use Profile-Specific Configuration Files to review the decision. Write a corrected implementation or design sketch that follows application-{profile}.properties/yml files override the base configuration when that profile is active., then name one edge case you would check.

Show hints
  • Start with Profile-Specific Configuration Files and compare your approach with Profile-specific datasource configuration and a profile-restricted bean. Identify the decision or behavior the lesson recommends.
  • Walk through your scenario step by step. Confirm that it follows application-{profile}.properties/yml files override the base configuration when that profile is active. and that it would catch the failure described by Forgetting to set spring.profiles.active anywhere and being confused why "default" configuration always loads instead of the intended dev/prod file..

Quick quiz

1. Which choice best describes the recommended approach in Spring Boot Profiles?

  • A. Forgetting to set spring.profiles.active anywhere and being confused why "default" configuration always loads instead of the intended dev/prod file.
  • B. Marking the fake implementation @Profile("dev") instead of @Profile("!prod"). A new staging or demo profile then has no PaymentGateway bean at all, and startup fails.
  • C. application-{profile}.properties/yml files override the base configuration when that profile is active.
Show answer

C. This is the recommended approach: application-{profile}.properties/yml files override the base configuration when that profile is active. Avoid: Forgetting to set spring.profiles.active anywhere and being confused why "default" configuration always loads instead of the intended dev/prod file.

Interview questions

Why does this principle matter in Spring Boot Profiles: application-{profile}.properties/yml files override the base configuration when that profile is active.

Show answer

application-{profile}.properties/yml files override the base configuration when that profile is active. Apply this in Profile-Specific Configuration Files; the example Profile-specific datasource configuration and a profile-restricted bean shows one way to do it. Avoid Forgetting to set spring.profiles.active anywhere and being confused why "default" configuration always loads instead of the intended dev/prod file. and explain how your implementation or design prevents that failure.

Key Points to Remember

  • application-{profile}.properties/yml files override the base configuration when that profile is active.
  • spring.profiles.active selects the active profile(s), settable via property, environment variable, or command-line argument.
  • @Profile restricts a bean definition to specific named profiles, useful for swapping implementations between environments.
  • Keep shared configuration in the base file and only environment-specific differences in profile files.
  • @Profile accepts !, & and | expressions; "!prod" is the safe way to declare fakes.
  • spring.profiles.group.<name> activates several profiles at once, and the last active profile wins a conflicting key.

Practice in the playground

Write and run your own code for this topic, or start from the exercise or an example. Runs plain Java 17. Frameworks such as Spring, and databases, are not available here. Your code for each topic is kept in this browser.

Run the examples

Change an input, predict the result, then compare it with the output. Explain why the result changes.

These examples need a JDK project with their libraries on your computer, so they can be copied but not run here. Codelab runs Python, FastAPI backends with SQLite, plain Java 17 and HTML/CSS/JavaScript.

WebNest Studio

Written by

WebNest Studio Engineering Team

Practical lessons from developers working with Java, Python, React, databases and production systems.

Led by Vansh Duggal, Founder, WebNest Studio · Full-stack & software engineering