application.properties, YAML, profiles, property precedence, environment variables, @Value, @ConfigurationProperties, spring.config.location, and spring.config.import.
Imagine the same Spring Boot application is deployed to three environments:
| Environment | Database URL | Server Port |
|---|---|---|
| Development | localhost database | 8080 |
| QA | QA database | 8081 |
| Production | Production database | 80/443 through infrastructure |
You do not want to change Java code every time the environment changes.
Spring Boot supports many configuration sources, including:
application.propertiesapplication.yaml / application.ymlSPRING_APPLICATION_JSONSpring Boot combines these sources into Spring's Environment. Later, higher-precedence sources can override values from earlier sources.
Think of the Environment as a large configuration box:
You can access values using:
| Approach | Best for |
|---|---|
@Value | Small number of individual values |
Environment | Programmatic access to properties |
@ConfigurationProperties | Groups of related configuration values |
A normal Spring Boot project can have:
src/main/resources/application.properties
Example:
server.port=8080 spring.application.name=my-api app.message=Hello Spring Boot app.max-users=100
Spring Boot automatically loads standard application configuration files from its supported config locations.
You can also use YAML:
server:
port: 8080
spring:
application:
name: my-api
app:
message: Hello Spring Boot
max-users: 100
YAML is especially convenient when configuration is hierarchical.
.properties and YAML exist in the same location, the properties format takes precedence.
Spring Boot uses a specific PropertySource order. A source later in the order can override a value supplied by an earlier source.
| Order | Source |
|---|---|
| 1 | Default properties from SpringApplication.setDefaultProperties(Map) |
| 2 | @PropertySource annotations |
| 3 | Config data such as application.properties |
| 4 | RandomValuePropertySource for random.* |
| 5 | OS environment variables |
| 6 | Java system properties |
| 7 | JNDI attributes |
| 8 | ServletContext init parameters |
| 9 | ServletConfig init parameters |
| 10 | SPRING_APPLICATION_JSON / spring.application.json |
| 11 | Command-line arguments |
| 12 | Test properties attribute |
| 13 | @DynamicPropertySource |
| 14 | @TestPropertySource |
| 15 | Devtools global settings |
Suppose application.properties contains:
server.port=8080
Now start the application with:
java -jar app.jar --server.port=9000
The command-line value wins, so the application uses:
server.port=9000
Command-line properties are added to the Spring Environment by default and have higher precedence than file-based configuration.
If you do not want command-line options to be added to the Environment, configure:
SpringApplication application =
new SpringApplication(MyApplication.class);
application.setAddCommandLineProperties(false);
application.run(args);
Spring Boot automatically searches for application.properties and application.yaml in several standard locations.
| Area | Location |
|---|---|
| Classpath | Classpath root |
| Classpath | Classpath /config |
| Current directory | ./ |
| Current directory | ./config/ |
| Current directory | Immediate child directories of ./config/ |
External configuration locations have higher precedence than earlier default locations, allowing deployment-specific values to override packaged defaults.
Suppose your JAR contains:
application.properties server.port=8080 app.environment=default
On the production server, you can provide:
./config/application.properties app.environment=production server.port=9090
You can deploy the same JAR to multiple environments without rebuilding it.
By default, Spring Boot looks for the application basename.
You can change it with:
java -jar myproject.jar --spring.config.name=myproject
Spring Boot can then look for:
myproject.properties myproject.yaml
spring.config.name is needed very early during startup, so it must be supplied as an environment property, system property, or command-line argument.
spring.config.location lets you explicitly tell Spring Boot where to search for configuration.
java -jar app.jar \ --spring.config.location=optional:classpath:/default.properties,optional:classpath:/override.properties
It accepts a comma-separated list of locations.
spring.config.location replaces the default search locations. It does not simply add another location.
If you want to keep the normal default locations and add more locations, use:
java -jar app.jar \ --spring.config.additional-location=optional:file:./custom-config/
| Property | Effect |
|---|---|
spring.config.location | Replaces the default locations. |
spring.config.additional-location | Adds extra locations after the defaults. |
By default, a configured location that does not exist can cause:
ConfigDataLocationNotFoundException
If a location is optional, prefix it with:
optional:
Example:
spring.config.import=optional:file:./myconfig.properties
Now the application can start even if that file does not exist.
You can configure Spring Boot to ignore all missing config-data location errors:
spring.config.on-not-found=ignore
This can also be configured through SpringApplication.setDefaultProperties(...).
Wildcard locations are useful when configuration is split across multiple directories.
Example:
/config/
redis/
application.properties
mysql/
application.properties
A wildcard location such as:
config/*/
allows the immediate subdirectories to be searched.
Spring Boot includes config/*/ among its default external search locations.
Spring Boot supports profile-specific files using:
application-{profile}.properties
application-{profile}.yaml
Example:
application.properties application-dev.properties application-qa.properties application-prod.properties
spring.profiles.active=prod
Or:
java -jar app.jar --spring.profiles.active=prod
If application.properties says:
app.message=Default message
and application-prod.properties says:
app.message=Production message
with the prod profile active, the production value overrides the default value.
You can activate multiple profiles:
spring.profiles.active=prod,live
When multiple profiles are active, a last-wins strategy applies at the location-group level. Values from the later profile can override earlier profile values.
prod,live → if both define app.message, the live value can override the prod value.
When using spring.config.location, commas and semicolons have different meanings.
| Separator | Meaning |
|---|---|
, | Locations are processed in sequence. |
; | Locations belong to the same location group. |
Example:
spring.config.location=classpath:/cfg/,classpath:/ext/
Compared with:
spring.config.location=classpath:/cfg/;classpath:/ext/
This becomes especially important when profile-specific files exist in multiple locations.
Configuration values can refer to other configuration values using:
${name}
Example:
app.name=My Store
app.welcome=Welcome to ${app.name}
The resulting value of app.welcome becomes:
Welcome to My Store
app.timeout=${TIMEOUT:30}
This means:
TIMEOUT if it exists.30.@Value is useful when you need one or two values.
@Component
public class MyBean {
@Value("${app.message}")
private String message;
@Value("${app.max-users}")
private int maxUsers;
}
Configuration:
app.message=Hello app.max-users=100
@Value is simple. For a group of related application settings, @ConfigurationProperties is usually cleaner.
@Component
public class MyService {
private final Environment environment;
public MyService(Environment environment) {
this.environment = environment;
}
public void printConfig() {
String url =
environment.getProperty("app.url");
System.out.println(url);
}
}
This approach is useful when you need programmatic or dynamic property lookup.
Suppose you have:
app: name: My Store url: https://example.com timeout: 30s max-users: 100
Create a configuration object:
@ConfigurationProperties(prefix = "app")
public class AppProperties {
private String name;
private String url;
private Duration timeout;
private int maxUsers;
// getters and setters
}
Then use it in your service:
@Service
public class MyService {
private final AppProperties properties;
public MyService(AppProperties properties) {
this.properties = properties;
}
public void connect() {
System.out.println(properties.getUrl());
}
}
@Value → one/few values@ConfigurationProperties → a complete configuration group
One major advantage of @ConfigurationProperties is that configuration can map into Java types instead of leaving everything as strings.
| Configuration | Java type |
|---|---|
timeout: 30s | Duration |
max-users: 100 | int |
| Boolean property | boolean |
| URL-like value | Appropriate structured type where supported |
| List values | List<T> |
| Map values | Map<K,V> |
Configuration properties can also be represented using immutable objects and constructor-based binding.
@ConfigurationProperties("app")
public class AppProperties {
private final String name;
private final int maxUsers;
public AppProperties(String name, int maxUsers) {
this.name = name;
this.maxUsers = maxUsers;
}
public String getName() {
return name;
}
public int getMaxUsers() {
return maxUsers;
}
}
This style is useful when you want configuration objects that should not be modified after creation.
Spring Boot's configuration-property binding is flexible about common naming conventions.
| Style | Example |
|---|---|
| Kebab case | max-users |
| Environment variable style | MAX_USERS |
| Typical Java camelCase | maxUsers |
For environment variables, periods are generally replaced with underscores, and naming is commonly converted to uppercase.
spring.config.name
becomes:
SPRING_CONFIG_NAME
Environment variables are especially useful in Docker and Kubernetes.
export SERVER_PORT=8085 export SPRING_PROFILES_ACTIVE=prod
Spring Boot can map these environment variables to configuration properties.
You can place multiple properties inside one JSON object.
SPRING_APPLICATION_JSON='{"my":{"name":"test"}}' java -jar myapp.jar
Spring Boot exposes this as:
my.name=test
The same JSON can be supplied as a Java system property:
java -Dspring.application.json='{"my":{"name":"test"}}' -jar myapp.jar
Or as a command-line argument:
java -jar myapp.jar --spring.application.json='{"my":{"name":"test"}}'
null is treated as a missing value by Spring's property resolver, so JSON cannot override a lower-precedence property with null.
spring.config.import allows one configuration file to import another configuration source.
spring.config.import=optional:file:./myconfig.properties
This is useful when configuration is split into separate files or comes from another supported configuration source.
Some cloud platforms mount files without file extensions. Spring Boot lets you provide an extension hint.
spring.config.import=file:/etc/config/myconfig[.yaml]
Equivalent explicit form:
spring.config.import=file:/etc/config/myconfig[extension=.yaml]
Multiple attributes can also be supplied:
spring.config.import=file:/etc/config/myconfig[extension=.yaml][encoding=utf-8]
Spring Boot can treat the contents of an environment variable as a configuration file.
Suppose:
MY_CONFIGURATION=" my.name=Service1 my.cluster=Cluster1 "
Then:
spring.config.import=env:MY_CONFIGURATION
The imported properties become part of the Spring Environment.
.properties, and an extension can also be specified when needed.Configuration trees are useful when secrets or configuration values are mounted as individual files, especially in Kubernetes or Docker.
Example directory:
/etc/config/
myapp/
username
password
Import it with:
spring.config.import=optional:configtree:/etc/config/
The file names become property names.
myapp.username myapp.password
Suppose Docker mounts a secret:
/run/secrets/db.password
Import the directory:
spring.config.import=optional:configtree:/run/secrets/
The property db.password becomes available to the Spring Environment.
Configuration trees can be combined with wildcard locations when configuration is split across mounted directories.
Directory names and file names under a configuration tree form property names. Dot notation in file names is also supported.
Spring Boot does provide extension points such as EnvironmentPostProcessor that can modify values in the Spring Environment before the application starts.
For secure external secret storage, projects such as Spring Cloud Vault can be used.
@ConfigurationProperties can also be applied to public @Bean methods. This is useful when configuring third-party components that you cannot modify.
@Bean
@ConfigurationProperties("my.datasource")
public DataSource dataSource() {
return DataSourceBuilder.create().build();
}
This allows external configuration to bind into a third-party object.
Use a meaningful prefix to group settings:
my:
datasource:
url: jdbc:mysql://localhost/shop
username: app
password: secret
Then bind:
@ConfigurationProperties("my.datasource")
Spring Boot can generate configuration metadata for your own @ConfigurationProperties classes.
This metadata helps IDEs provide:
@ConfigurationProperties classes make configuration easier to discover and harder to mistype.| Concept | Meaning |
|---|---|
PropertySource | One source of configuration values. |
Environment | Combines property sources and resolves properties. |
@Value | Injects a property into a bean. |
@ConfigurationProperties | Binds a group of properties to a structured object. |
@PropertySource can add a property source, but it is added to the Environment only during context refresh.
logging.* and spring.main.*, which are read before the context refresh begins.
For normal Spring Boot application configuration, prefer the standard Config Data mechanisms such as application.properties, YAML, and spring.config.import.
When a property unexpectedly has a value, two useful Spring Boot Actuator endpoints are:
| Endpoint | Useful for |
|---|---|
env | Inspecting environment properties and property sources. |
configprops | Inspecting bound @ConfigurationProperties objects. |
These are useful when debugging precedence and binding problems.
spring.application.name=my-api server.port=8080 app.name=My API app.timeout=30s
app.environment=development app.database-url=jdbc:postgresql://localhost/dev
app.environment=qa app.database-url=jdbc:postgresql://qa-db/myapp
app.environment=production app.database-url=jdbc:postgresql://prod-db/myapp
java -jar app.jar --spring.profiles.active=qa
java -jar app.jar \ --spring.profiles.active=qa \ --app.timeout=60s
A common containerized architecture is:
The application artifact remains the same while deployment infrastructure supplies environment-specific configuration.
| Mistake | Better understanding |
|---|---|
| Hardcoding database URLs in Java. | Externalize them. |
| Putting production passwords in Git. | Use environment/secret management. |
Confusing location and additional-location. | location replaces defaults; additional-location adds to them. |
| Assuming a missing optional file should fail startup. | Use optional: when absence is acceptable. |
Using @Value for dozens of related properties. | Prefer @ConfigurationProperties. |
Assuming @PropertySource can configure every Boot property. | Some properties are read before context refresh. |
| Assuming JSON null can override another source. | Null is treated as missing by the property resolver. |
| Using different config formats everywhere. | Prefer one format consistently. |
@ConfigurationProperties for structured application settings.@Value for a small number of simple properties.spring.config.additional-location when you want to preserve default locations.optional: only when missing configuration is genuinely acceptable.| Requirement | Recommended approach |
|---|---|
| One simple property | @Value |
| Many related properties | @ConfigurationProperties |
| Programmatic lookup | Environment |
| Different environments | Profiles |
| Docker/Kubernetes runtime values | Environment variables |
| Mounted secrets | Configuration trees |
| Extra external config directory | spring.config.additional-location |
| Replace default config locations | spring.config.location |
| Optional imported file | optional: |
| Multiple config sources inside one JSON value | SPRING_APPLICATION_JSON |
| Import another supported config source | spring.config.import |
Environment?PropertySource?application.properties or a command-line argument?spring.config.location and spring.config.additional-location?optional: prefix do?@Value and @ConfigurationProperties?Environment?SPRING_APPLICATION_JSON?spring.config.import?@PropertySource for logging.* and spring.main.*?application.properties with server port, application name, and a custom app.message.application-dev.properties and application-prod.properties.@Value.@ConfigurationProperties.SPRING_PROFILES_ACTIVE=prod and explain what changes.spring.config.additional-location.db.username and db.password.| Concept | Example |
|---|---|
| Properties file | application.properties |
| YAML | application.yaml |
| Profile | application-prod.properties |
| Activate profile | --spring.profiles.active=prod |
| Custom basename | --spring.config.name=myproject |
| Replace config locations | --spring.config.location=... |
| Add config locations | --spring.config.additional-location=... |
| Optional location | optional:file:./config.properties |
| Import config | spring.config.import=... |
| Environment variable | SERVER_PORT=8080 |
| JSON configuration | SPRING_APPLICATION_JSON='{"app":{"name":"demo"}}' |
| Single value injection | @Value("${app.name}") |
| Structured binding | @ConfigurationProperties("app") |
| Programmatic lookup | Environment#getProperty() |
| Mounted config/secrets | configtree:/etc/config/ |
application.properties / YAML → basic configuration@Value → simple individual values@ConfigurationProperties → structured configurationspring.config.location → replace default locationsspring.config.additional-location → add locationsspring.config.import → import configuration