Skip to main content

Week 2 Day 3 — Spring Profiles: dev, test, prod, and @Profile

Goal

Today I want to understand Spring profiles.

Main questions:

  1. What is a Spring profile?
  2. Why do we need profiles?
  3. How do application-dev.yml, application-test.yml, and application-prod.yml work?
  4. How do I activate a profile?
  5. What does @Profile do?
  6. Can I have multiple active profiles?
  7. What does @Profile("!prod") mean?
  8. How do profiles affect beans?
  9. How do profiles affect configuration files?
  10. What are common exam traps?

1. Quick Review from Day 2

In Day 2, I learned:

  • External configuration keeps environment-specific values outside Java code.
  • Spring Boot reads configuration from application.properties, application.yml, environment variables, command-line arguments, and more.
  • @Value injects one value.
  • @ConfigurationProperties binds grouped configuration.
  • Environment variables can override file configuration.
  • Secrets should not be committed to Git.

Memory sentence:

@Value is for one value.
@ConfigurationProperties is for grouped config.

Today I learn how to use different configuration for different environments.


2. What Is a Spring Profile?

A Spring profile is a way to activate different configuration or beans for different environments.

Common profiles:

dev
test
prod
local
docker
ci
staging

Simple definition:

A Spring profile is a named environment mode that controls which configuration and beans are active.

Example:

dev = local development
test = automated tests
prod = production

3. Why Do We Need Profiles?

Different environments need different settings.

Example:

Local development

spring:
datasource:
url: jdbc:postgresql://localhost:5432/app_dev

Production

spring:
datasource:
url: jdbc:postgresql://prod-db:5432/app_prod

Same code.

Different configuration.

This is exactly what profiles help with.


4. Real Example

Imagine a Spring Boot app with email sending.

In development, I do not want to send real emails.

In production, I want real emails.

So I can have:

dev profile -> FakeEmailSender
prod profile -> RealEmailSender

Example:

public interface EmailSender {
void send(String to, String message);
}
@Service
@Profile("dev")
public class FakeEmailSender implements EmailSender {

@Override
public void send(String to, String message) {
System.out.println("Fake email to " + to);
}
}
@Service
@Profile("prod")
public class RealEmailSender implements EmailSender {

@Override
public void send(String to, String message) {
// send real email
}
}

If dev is active, Spring creates FakeEmailSender.

If prod is active, Spring creates RealEmailSender.


5. Profile-Specific Config Files

Spring Boot supports profile-specific configuration files.

Common files:

application.yml
application-dev.yml
application-test.yml
application-prod.yml

Base file:

## application.yml
spring:
application:
name: klarsync

server:
port: 8080

Development file:

## application-dev.yml
spring:
datasource:
url: jdbc:postgresql://localhost:5432/klarsync_dev
username: postgres
password: postgres

logging:
level:
org.springframework: INFO

Production file:

## application-prod.yml
spring:
datasource:
url: jdbc:postgresql://prod-db:5432/klarsync
username: ${DB_USERNAME}
password: ${DB_PASSWORD}

logging:
level:
org.springframework: WARN

6. How Spring Loads Profile Config

If no profile is active, Spring loads:

application.yml

If dev profile is active, Spring loads:

application.yml
application-dev.yml

If prod profile is active, Spring loads:

application.yml
application-prod.yml

Profile-specific values can override base values.

Memory sentence:

application.yml is base config. application-dev.yml overrides it when dev is active.


7. Example: Override Values

Base file:

## application.yml
server:
port: 8080

app:
name: klarsync
email-enabled: true

Dev file:

## application-dev.yml
server:
port: 8081

app:
email-enabled: false

If dev is active:

server.port = 8081
app.name = klarsync
app.email-enabled = false

server.port is overridden.

app.email-enabled is overridden.

app.name remains from the base file.


8. How to Activate a Profile

There are several ways.

Option 1 — In application.yml

spring:
profiles:
active: dev

This works, but it is not always recommended for production because profile selection should often come from the environment.


Option 2 — Command Line

java -jar app.jar --spring.profiles.active=prod

Option 3 — Environment Variable

SPRING_PROFILES_ACTIVE=prod java -jar app.jar

This is common in Docker and production.


Option 4 — IDE Run Configuration

In IntelliJ, I can add:

-Dspring.profiles.active=dev

or environment variable:

SPRING_PROFILES_ACTIVE=dev

Option 5 — Test Annotation

In tests:

@SpringBootTest
@ActiveProfiles("test")
class TaskServiceTest {
}

This activates the test profile for the test.


9. Best Practice for Profile Activation

For local development, using dev in IDE is fine.

For production, prefer environment variables or deployment config.

Good:

SPRING_PROFILES_ACTIVE=prod

Avoid committing production profile activation into Git:

spring:
profiles:
active: prod

Why?

Because it can accidentally make local or test environments use production configuration.


10. @Profile

@Profile controls whether a bean is created for a specific profile.

Example:

@Service
@Profile("dev")
public class FakeEmailSender implements EmailSender {
}

This bean is only created when the dev profile is active.

Example:

@Service
@Profile("prod")
public class RealEmailSender implements EmailSender {
}

This bean is only created when the prod profile is active.


11. @Profile on @Bean Methods

@Profile can also be used on @Bean methods.

@Configuration
public class EmailConfig {

@Bean
@Profile("dev")
public EmailSender fakeEmailSender() {
return new FakeEmailSender();
}

@Bean
@Profile("prod")
public EmailSender realEmailSender() {
return new RealEmailSender();
}
}

If dev is active, Spring creates fakeEmailSender.

If prod is active, Spring creates realEmailSender.


12. @Profile on Configuration Classes

You can put @Profile on a whole configuration class.

@Configuration
@Profile("dev")
public class DevConfig {

@Bean
public DataInitializer dataInitializer() {
return new DataInitializer();
}
}

This whole configuration class is active only in dev.

If dev is not active, Spring ignores this configuration class and its beans.


13. Multiple Profiles

Spring can have multiple active profiles.

Example:

java -jar app.jar --spring.profiles.active=dev,docker

Active profiles:

dev
docker

Spring then loads:

application.yml
application-dev.yml
application-docker.yml

Beans with either profile may become active.

Example:

@Profile("dev")

active if dev is active.

@Profile("docker")

active if docker is active.


14. Profile Expressions

@Profile supports simple expressions.

Not profile

@Profile("!prod")

Meaning:

Active when prod is NOT active.

Example:

@Service
@Profile("!prod")
public class ConsoleEmailSender implements EmailSender {
}

This bean is active in dev, test, or other non-prod environments.


AND expression

@Profile("dev & docker")

Meaning:

Active only when both dev and docker are active.

OR expression

@Profile("dev | test")

Meaning:

Active when dev or test is active.

15. Common Profile Expressions

ExpressionMeaning
devactive when dev is active
prodactive when prod is active
!prodactive when prod is not active
`devtest`active when dev or test is active
dev & dockeractive when both dev and docker are active

16. Real Example: Dev vs Prod Email

Interface:

public interface EmailSender {
void send(String to, String message);
}

Dev implementation:

@Service
@Profile("dev")
public class ConsoleEmailSender implements EmailSender {

@Override
public void send(String to, String message) {
System.out.println("DEV email to " + to + ": " + message);
}
}

Prod implementation:

@Service
@Profile("prod")
public class SmtpEmailSender implements EmailSender {

@Override
public void send(String to, String message) {
// send via SMTP provider
}
}

Service:

@Service
public class RegistrationService {

private final EmailSender emailSender;

public RegistrationService(EmailSender emailSender) {
this.emailSender = emailSender;
}
}

If dev is active:

ConsoleEmailSender is injected.

If prod is active:

SmtpEmailSender is injected.

17. What Happens If No Profile Matches?

Example:

@Service
@Profile("dev")
public class ConsoleEmailSender implements EmailSender {
}
@Service
@Profile("prod")
public class SmtpEmailSender implements EmailSender {
}

Service:

@Service
public class RegistrationService {

public RegistrationService(EmailSender emailSender) {
}
}

If no profile is active:

No EmailSender bean exists.
Application fails to start.

Typical error:

No qualifying bean of type 'EmailSender' available

Fix options:

  1. Activate a profile.
  2. Provide a default bean.
  3. Use @Profile("default").
  4. Use @Profile("!prod") for non-production default.
  5. Make the dependency optional if truly optional.

18. Default Profile

Spring has a default profile named:

default

If no profile is active, beans with:

@Profile("default")

can be active.

Example:

@Service
@Profile("default")
public class DefaultEmailSender implements EmailSender {
}

This bean is active only when no other profile is active.

Important:

The default profile is used when no explicit profile is active.


19. @Profile("default") vs @Profile("!prod")

@Profile("default")

Active only when no profile is explicitly active.

@Profile("default")

Useful for a basic fallback.

@Profile("!prod")

Active whenever prod is not active.

@Profile("!prod")

Active in:

dev
test
local
docker
default

Not active in:

prod

Memory sentence:

default means no profile is active. !prod means any profile except prod.


20. Profile-Specific Values with @ConfigurationProperties

YAML:

## application.yml
external-api:
base-url: https://api.default.com
timeout-seconds: 5
## application-prod.yml
external-api:
base-url: https://api.production.com
timeout-seconds: 20

Properties class:

@ConfigurationProperties(prefix = "external-api")
public record ExternalApiProperties(
String baseUrl,
int timeoutSeconds
) {
}

If prod is active:

baseUrl = https://api.production.com
timeoutSeconds = 20

The properties class does not need profile logic.

Spring binds the active configuration automatically.


21. Profile Groups

Spring Boot supports profile groups.

Example:

spring:
profiles:
group:
local:
- dev
- debug
- mock-email

If I activate:

--spring.profiles.active=local

Spring also activates:

dev
debug
mock-email

This is useful when one profile should activate a group of profiles.


22. Profile Include

You may also see profile include configuration.

Example:

spring:
profiles:
include: common

This includes another profile.

Profile groups are often clearer for modern apps.

Important for exam:

Know that profiles can be combined or grouped, but core exam questions usually focus on @Profile and profile-specific files.


23. Profiles and Tests

In tests, use:

@SpringBootTest
@ActiveProfiles("test")
class UserServiceTest {
}

Then Spring loads:

application.yml
application-test.yml

And beans with:

@Profile("test")

can be active.

Example:

@TestConfiguration
@Profile("test")
public class TestConfig {
}

24. Why Use a Test Profile?

A test profile can use:

test database
fake email sender
mock external APIs
short token expiration
disabled scheduled jobs
faster configuration

Example:

## application-test.yml
spring:
datasource:
url: jdbc:h2:mem:testdb

app:
email-enabled: false

25. Profiles and Database Configuration

Example base file:

## application.yml
spring:
jpa:
hibernate:
ddl-auto: validate

Dev file:

## application-dev.yml
spring:
datasource:
url: jdbc:postgresql://localhost:5432/app_dev
username: postgres
password: postgres

Test file:

## application-test.yml
spring:
datasource:
url: jdbc:h2:mem:testdb

Prod file:

## application-prod.yml
spring:
datasource:
url: ${DB_URL}
username: ${DB_USERNAME}
password: ${DB_PASSWORD}

Same code.

Different config.


26. Profiles and Logging

Dev:

## application-dev.yml
logging:
level:
org.springframework.security: DEBUG
de.klarsync: DEBUG

Prod:

## application-prod.yml
logging:
level:
org.springframework.security: WARN
de.klarsync: INFO

Development can be more verbose.

Production should usually be less noisy.


27. Profiles and Feature Flags

Example:

## application-dev.yml
feature:
new-dashboard: true
fake-email: true
## application-prod.yml
feature:
new-dashboard: false
fake-email: false

Properties:

@ConfigurationProperties(prefix = "feature")
public record FeatureProperties(
boolean newDashboard,
boolean fakeEmail
) {
}

Then services can decide behavior based on config.


28. Profiles vs Feature Flags

Profiles are for environment-level configuration.

Feature flags are for enabling or disabling application features.

Use profiles for:

dev/test/prod environment differences

Use feature flags for:

turning a feature on/off

Do not create too many profiles just for small feature switches.


29. Profiles vs Conditions

@Profile is a kind of condition based on active profiles.

Spring Boot also has more specific conditions:

@ConditionalOnProperty
@ConditionalOnMissingBean
@ConditionalOnClass

Example:

@Bean
@ConditionalOnProperty(name = "feature.audit.enabled", havingValue = "true")
public AuditService auditService() {
return new AuditService();
}

Use @Profile for environment.

Use @ConditionalOnProperty for feature/config-based conditions.


30. Common Mistake: Profile Active in File

This can be dangerous:

## application.yml
spring:
profiles:
active: prod

Why?

Because every environment that uses this file may start with prod.

Better:

Set active profile from environment variable, command line, Docker, CI/CD, or IDE config.

For local development, it is okay to configure the IDE to use dev.


31. Common Mistake: Both Dev and Prod Active

Command:

java -jar app.jar --spring.profiles.active=dev,prod

This can cause confusing behavior.

Example:

@Profile("dev")
FakeEmailSender

and:

@Profile("prod")
RealEmailSender

Both become active.

If both implement EmailSender, injection becomes ambiguous.

Fix:

  • avoid incompatible profiles together
  • use profile expressions
  • use @Primary or @Qualifier carefully
  • design profile combinations intentionally

32. Common Mistake: Missing Default Bean

If I only define:

@Profile("dev")
FakeEmailSender

and:

@Profile("prod")
RealEmailSender

then with no active profile, no EmailSender exists.

If the app should run locally without explicit profile, add:

@Profile("default")

or:

@Profile("!prod")

depending on your goal.


33. Common Mistake: Expecting application-prod.yml to Load Automatically

This file:

application-prod.yml

does not load just because it exists.

It loads only when the prod profile is active.

Memory sentence:

Profile-specific files load only when their profile is active.


34. Common Mistake: Secrets in Profile Files

Bad:

## application-prod.yml
spring:
datasource:
password: real-production-password

Better:

## application-prod.yml
spring:
datasource:
password: ${DB_PASSWORD}

Then set:

DB_PASSWORD=real-secret

Do not commit real secrets.


35. Real Question: Which Config Wins?

Base:

## application.yml
server:
port: 8080

Dev:

## application-dev.yml
server:
port: 8081

Command:

java -jar app.jar --spring.profiles.active=dev --server.port=9090

Final value:

server.port = 9090

Why?

Because command-line argument overrides profile file value.

General idea:

Higher-priority external config overrides lower-priority config.


36. Real Exam Question: Active Profile

Question:

java -jar app.jar --spring.profiles.active=dev

Which files are loaded?

Answer:

Spring loads the base configuration and the dev-specific configuration:

application.yml
application-dev.yml

or:

application.properties
application-dev.properties

depending on file format.


37. Real Exam Question: @Profile

Question:

@Service
@Profile("prod")
public class RealEmailSender implements EmailSender {
}

When is this bean created?

Answer:

This bean is created only when the prod profile is active.


38. Real Exam Question: Negative Profile

Question:

@Service
@Profile("!prod")
public class ConsoleEmailSender implements EmailSender {
}

When is this bean created?

Answer:

This bean is created when the prod profile is not active.


39. Real Exam Question: Multiple Profiles

Question:

java -jar app.jar --spring.profiles.active=dev,docker

Is this valid?

Answer:

Yes. Spring can have multiple active profiles. Both dev and docker are active.


40. Real Exam Question: No Matching Profile

Question:

@Service
@Profile("dev")
public class DevPaymentProvider implements PaymentProvider {
}
@Service
@Profile("prod")
public class ProdPaymentProvider implements PaymentProvider {
}

No profile is active.

What happens if another bean requires PaymentProvider?

Answer:

No PaymentProvider bean is created, so Spring fails to start with a missing bean error.


41. Real Exam Question: Default Profile

Question:

What is the default profile?

Answer:

The default profile is active when no other profile is explicitly active. Beans annotated with @Profile("default") can be created when no profile is set.


42. Real Exam Question: default vs !prod

Question:

What is the difference between @Profile("default") and @Profile("!prod")?

Answer:

@Profile("default") is active only when no profile is explicitly active. @Profile("!prod") is active whenever the prod profile is not active, including dev, test, local, or default mode.


43. Real Exam Question: application-prod.yml

Question:

Does application-prod.yml load automatically because the file exists?

Answer:

No. It loads only when the prod profile is active.


44. Real Exam Question: Test Profile

Question:

How do I activate the test profile in a Spring Boot test?

Answer:

Use:

@ActiveProfiles("test")

Example:

@SpringBootTest
@ActiveProfiles("test")
class MyTest {
}

45. Real Exam Question: Profiles vs Properties

Question:

Should I use profiles or feature flags for turning on one small feature?

Answer:

Usually use a feature flag or property, not a new profile. Profiles are better for environment-level configuration such as dev, test, prod, or docker.


46. Interview Answer

Question:

What are Spring profiles?

Good answer:

Spring profiles allow us to define different beans and configuration for different environments, such as dev, test, and prod. A profile can activate profile-specific configuration files like application-dev.yml and beans annotated with @Profile("dev"). This helps us run the same application code with different environment-specific settings.


47. Interview Answer

Question:

How do profile-specific configuration files work?

Good answer:

Spring Boot always loads the base configuration file, such as application.yml. If a profile is active, for example dev, Spring also loads application-dev.yml. Values from the profile-specific file can override values from the base file. The profile can be activated with spring.profiles.active, an environment variable, a command-line argument, or @ActiveProfiles in tests.


48. Interview Answer

Question:

What does @Profile do?

Good answer:

@Profile controls whether a bean or configuration class is active for a specific profile. For example, @Profile("dev") means the bean is created only when the dev profile is active. It can be used on classes, configuration classes, or @Bean methods. It is useful when different environments need different bean implementations.


49. Interview Answer

Question:

How do you activate a profile in Spring Boot?

Good answer:

A profile can be activated in several ways. For example, with a command-line argument like --spring.profiles.active=prod, with an environment variable like SPRING_PROFILES_ACTIVE=prod, in an IDE run configuration, or in tests using @ActiveProfiles("test"). In production, it is usually better to activate profiles through environment or deployment configuration rather than hardcoding them in application.yml.


50. Interview Answer

Question:

What problems can profiles cause?

Good answer:

Profiles can cause missing bean errors if no active profile matches the required bean. They can also cause ambiguity if multiple profiles are active and multiple beans of the same type are created. Another common problem is expecting a profile-specific file like application-prod.yml to load automatically even though the prod profile is not active. It is important to design profile usage clearly and avoid incompatible profiles being active together.


51. Tiny Code Practice

Create this interface:

public interface StorageService {
void store(String fileName);
}

Dev implementation:

@Service
@Profile("dev")
public class LocalStorageService implements StorageService {

@Override
public void store(String fileName) {
System.out.println("Storing locally: " + fileName);
}
}

Prod implementation:

@Service
@Profile("prod")
public class S3StorageService implements StorageService {

@Override
public void store(String fileName) {
System.out.println("Uploading to S3: " + fileName);
}
}

Usage:

@Service
public class DocumentService {

private final StorageService storageService;

public DocumentService(StorageService storageService) {
this.storageService = storageService;
}
}

Questions:

  1. Which bean is created when dev is active?
  2. Which bean is created when prod is active?
  3. What happens when no profile is active?

Answers:

  1. LocalStorageService
  2. S3StorageService
  3. No StorageService bean exists, so the app may fail unless a default bean is provided.

52. Tiny Bug Practice

Problem:

@Service
@Profile("dev")
public class FakeEmailSender implements EmailSender {
}
@Service
@Profile("prod")
public class RealEmailSender implements EmailSender {
}

Command:

java -jar app.jar --spring.profiles.active=dev,prod

Question:

What is the problem?

Answer:

Both FakeEmailSender and RealEmailSender become active. If another bean requires EmailSender, Spring finds two candidates and may fail with an ambiguity error unless one is marked @Primary, selected with @Qualifier, or the profiles are designed to be mutually exclusive.


Practice Questions and Answers

Question 1

What is a Spring profile?

Answer:

A Spring profile is a named environment mode that controls which configuration and beans are active.


Question 2

Why do we need profiles?

Answer:

Profiles are needed because different environments, such as local, test, and production, need different configuration and sometimes different bean implementations.


Question 3

Name three common profiles.

Answer:

Common profiles are:

dev
test
prod

Other common ones are:

local
docker
ci
staging

Question 4

What file is loaded for the dev profile?

Answer:

For the dev profile, Spring Boot loads:

application.yml
application-dev.yml

or the equivalent .properties files.


Question 5

Does application-prod.yml load automatically just because it exists?

Answer:

No. application-prod.yml loads only when the prod profile is active.


Question 6

How can I activate the prod profile from the command line?

Answer:

Use:

java -jar app.jar --spring.profiles.active=prod

Question 7

How can I activate the prod profile with an environment variable?

Answer:

Use:

SPRING_PROFILES_ACTIVE=prod java -jar app.jar

Question 8

How can I activate the test profile in a test?

Answer:

Use:

@ActiveProfiles("test")

Example:

@SpringBootTest
@ActiveProfiles("test")
class MyTest {
}

Question 9

What does @Profile("dev") mean?

Answer:

@Profile("dev") means the bean or configuration is active only when the dev profile is active.


Question 10

Can @Profile be used on @Bean methods?

Answer:

Yes. @Profile can be used on @Bean methods.


Question 11

Can @Profile be used on a whole configuration class?

Answer:

Yes. @Profile can be used on a whole configuration class. Then all beans in that configuration class are active only when the profile matches.


Question 12

Can multiple profiles be active at the same time?

Answer:

Yes. Multiple profiles can be active at the same time, for example dev,docker.


Question 13

What does @Profile("!prod") mean?

Answer:

@Profile("!prod") means the bean is active when the prod profile is not active.


Question 14

What does @Profile("dev | test") mean?

Answer:

@Profile("dev | test") means the bean is active when either dev or test is active.


Question 15

What does @Profile("dev & docker") mean?

Answer:

@Profile("dev & docker") means the bean is active only when both dev and docker profiles are active.


Question 16

What is the default profile?

Answer:

The default profile is active when no other profile is explicitly active.


Question 17

What is the difference between @Profile("default") and @Profile("!prod")?

Answer:

@Profile("default") is active only when no explicit profile is active. @Profile("!prod") is active whenever the prod profile is not active, including dev, test, local, or default mode.


Question 18

What happens if no profile-specific bean matches a required dependency?

Answer:

If no matching bean exists for a required dependency, Spring usually fails to start with a missing bean error.


Question 19

What happens if both dev and prod profiles are active and both create the same interface bean?

Answer:

Both beans may become active. If both implement the same interface and another bean needs that interface, Spring may fail with an ambiguity error unless the ambiguity is resolved.


Question 20

What is the difference between profiles and feature flags?

Answer:

Profiles are for environment-level configuration such as dev, test, and prod. Feature flags are for enabling or disabling specific application features.

Final Memory Sentences

  • A Spring profile controls which configuration and beans are active.
  • Common profiles are dev, test, and prod.
  • application.yml is base configuration.
  • application-dev.yml loads only when dev is active.
  • Profile-specific files override base configuration.
  • @Profile("dev") creates a bean only when dev is active.
  • @Profile("!prod") creates a bean when prod is not active.
  • Multiple profiles can be active at the same time.
  • @Profile("default") is active only when no explicit profile is active.
  • Activate profiles with command-line arguments, environment variables, IDE config, or @ActiveProfiles in tests.
  • Do not commit real production secrets into profile files.
  • Avoid activating incompatible profiles together.
  • Profiles are for environments.
  • Feature flags are for application features.