Configuration, Properties and Profiles
Spring Boot lets you keep configuration outside the code, so the same jar runs in development, test and production. Learn the property sources, their order, type-safe binding and profiles.
Properties or YAML
server.port=8081
spring.datasource.url=jdbc:postgresql://localhost:5432/shop
spring.datasource.username=app
app.mail.from=noreply@example.com
app.mail.retries=3server:
port: 8081
spring:
datasource:
url: jdbc:postgresql://localhost:5432/shop
username: app
app:
mail:
from: noreply@example.com
retries: 3Order of property sources (highest wins)
- 1. Command line arguments (--server.port=9000)
- 2. SPRING_APPLICATION_JSON environment property
- 3. Java system properties (-Dserver.port=9000)
- 4. OS environment variables (SERVER_PORT=9000)
- 5. Profile-specific files (application-prod.yml)
- 6. application.properties or application.yml
- 7. @PropertySource annotations
- 8. Default properties (SpringApplication.setDefaultProperties)
A value found in a higher source replaces the same key in a lower source. This is why you can override anything at deployment time with an environment variable without rebuilding the jar.
@Value: simple values
@Component
public class MailSender {
@Value("${app.mail.from}")
private String from;
@Value("${app.mail.retries:3}") // 3 is the default value
private int retries;
}@ConfigurationProperties: type-safe binding (recommended)
import jakarta.validation.constraints.Email;
import jakarta.validation.constraints.Min;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.validation.annotation.Validated;
@ConfigurationProperties(prefix = "app.mail")
@Validated
public record MailProperties(@Email String from, @Min(1) int retries) { }
@SpringBootApplication
@ConfigurationPropertiesScan // finds all @ConfigurationProperties classes
public class DemoApplication { }
@Service
public class MailService {
private final MailProperties props;
public MailService(MailProperties props) { this.props = props; }
}| Feature | @Value | @ConfigurationProperties |
|---|---|---|
| Best for | One or two values | A group of related values |
| Type safety | Low | High (records, classes, nested objects, lists) |
| Validation | No | Yes with @Validated |
| Relaxed binding | Limited | Full |
| IDE help | No | Yes (with the configuration processor) |
Relaxed binding: the property app.mail-from can be written as app.mailFrom, app.mail_from, or the environment variable APP_MAIL_FROM. All bind to the same field.
Profiles
A profile is a named set of configuration and beans, for example dev, test, prod.
# application.yml (common)
spring:
application:
name: shop
---
# application-dev.yml or a document in the same file
spring:
config:
activate:
on-profile: dev
datasource:
url: jdbc:h2:mem:devdb
---
spring:
config:
activate:
on-profile: prod
datasource:
url: jdbc:postgresql://db.prod:5432/shop@Configuration
public class MailConfig {
@Bean
@Profile("dev")
public MailClient fakeMailClient() { return new FakeMailClient(); }
@Bean
@Profile("prod")
public MailClient smtpMailClient() { return new SmtpMailClient(); }
}java -jar app.jar --spring.profiles.active=prod
SPRING_PROFILES_ACTIVE=prod java -jar app.jar
./mvnw spring-boot:run -Dspring-boot.run.profiles=devSecrets
- Never commit passwords or API keys to Git.
- Pass them through environment variables or a secret manager (Kubernetes Secrets, HashiCorp Vault, cloud secret services).
- Use spring.config.import to load extra config files, for example optional:file:./secrets.properties.
Inside the Environment: property sources chain
Environment
└─ MutablePropertySources (an ordered list. The FIRST source that has the key wins)
1. commandLineArgs --server.port=9000
2. servletConfigInitParams / servletContextInitParams
3. systemProperties -Dserver.port=9000
4. systemEnvironment SERVER_PORT=9000
5. random ${random.int}, ${random.uuid}
6. applicationConfig: [file:./config/application-prod.yml]
7. applicationConfig: [file:./application.yml]
8. applicationConfig: [classpath:/config/application.yml]
9. applicationConfig: [classpath:/application.yml]
10. @PropertySource sources, defaultProperties
env.getProperty("server.port") ─► asks the sources one by one, then converts the String to the requested type
${...} placeholders are resolved when values are injected, so one property can refer to another: app.url=http://${app.host}:${server.port}Where Boot looks for application.yml (later wins)
- classpath: root, then classpath: /config
- current directory: ./application.yml, then ./config/application.yml, then ./config/*/application.yml
- Profile files application-prod.yml at the same places win over the base file
- spring.config.location replaces the search locations. spring.config.additional-location adds to them.
- spring.config.import loads more files or external sources: optional:file:./secrets.properties, configtree:/run/secrets/ (Docker and Kubernetes secrets as files), optional:configserver:...
spring:
config:
import:
- optional:file:./local-overrides.yml # not an error if missing
- optional:configtree:/run/secrets/ # each file name = a property name, file content = value
profiles:
group:
prod: [prod-db, metrics, security-hardening] # activating "prod" activates all three
active: ${APP_PROFILE:dev} # from an environment variable with a defaultType-safe configuration with nested objects, lists, maps, Duration and DataSize
app:
storage:
root: /data/uploads
max-file-size: 10MB # DataSize
retention: 30d # Duration
clients:
- name: billing
url: https://billing.internal
timeout: 2s
- name: crm
url: https://crm.internal
timeout: 5s
feature-flags:
new-checkout: true
beta-search: false@ConfigurationProperties(prefix = "app")
@Validated
public record AppProperties(Storage storage, List<@Valid Client> clients, Map<String, Boolean> featureFlags) {
public record Storage(@NotBlank String root, DataSize maxFileSize, Duration retention) { }
public record Client(@NotBlank String name, @NotBlank String url, @DefaultValue("3s") Duration timeout) { }
}
@Service
public class FlagService {
private final AppProperties props;
public FlagService(AppProperties props) { this.props = props; }
public boolean enabled(String flag) { return props.featureFlags().getOrDefault(flag, false); }
public long maxBytes() { return props.storage().maxFileSize().toBytes(); }
}| Value in YAML | Java type | Notes |
|---|---|---|
| 10MB, 512KB | DataSize | toBytes(), toMegabytes() |
| 30d, 2h, 500ms, PT15M | Duration | Default unit can be set with @DurationUnit |
| a,b,c or a YAML list | List<String>, Set<String> | Comma separated works in properties files |
| key: value block | Map<String, X> | Keys keep their case inside quotes |
| nested block | Nested record or class | Validated with @Valid on the field |
Profiles in tests and deployments
@SpringBootTest
@ActiveProfiles("test") // loads application-test.yml
@TestPropertySource(properties = "app.feature-flags.new-checkout=true")
class CheckoutTest { }- Do not use profiles to hide behavior differences that tests never see. Test the prod profile combination in CI at least once.
- Environment variables are the standard way to change configuration in containers: SPRING_DATASOURCE_URL, APP_MAIL_FROM, SPRING_PROFILES_ACTIVE.
- Actuator env and configprops endpoints mask sensitive values by default (management.endpoint.env.show-values controls it in Boot 3.1 and newer). Keep them secured.
Common mistake: Putting production passwords in application.properties inside the jar. Use environment variables or a secret store, and keep the jar the same for all environments.
Interview questions
What is the difference between application.properties and bootstrap.properties?
application.properties is the normal configuration file. bootstrap.properties was used by Spring Cloud in older versions and is replaced by spring.config.import in new versions.
How do I set the active profile?
Use spring.profiles.active in a file, the SPRING_PROFILES_ACTIVE environment variable or the --spring.profiles.active argument.
Can I have more than one active profile?
Yes. Give a comma separated list, for example spring.profiles.active=prod,metrics.
Which wins: application.yml or an environment variable?
The environment variable. Higher sources in the chain override lower ones, and the packaged application.yml is one of the lowest.
How do I read Docker or Kubernetes secrets that are mounted as files?
Use spring.config.import=optional:configtree:/run/secrets/ so every file becomes a property.