Configuring Spring Boot Applications: application.properties, application.yml, and Environment Variables Explained
When developing a Spring Boot application, you need to manage various settings such as database connection information, server port, and log levels. These settings are managed through configuration files like application.properties or application.yml.
However, beginners often have endless questions like “Which file format should I use?”, “How do I load environment variables?”, and “When should I use @Value versus @ConfigurationProperties?”
This article provides a practical explanation, from the basics of configuration files to how to choose between them in real-world projects.
The Basics of Configuration Files - properties and yml
Spring Boot’s configuration files externalize settings such as database connection information and server port for management. Placing them in the src/main/resources directory means they are loaded automatically.
There are two main formats: application.properties (key=value format) and application.yml (YAML format).
# application.properties
server.port=8080
spring.datasource.url=jdbc:mysql://localhost:3306/mydb
# application.yml
server:
port: 8080
spring:
datasource:
url: jdbc:mysql://localhost:3306/mydb
For simple configurations, properties is easier to read; for deeply nested configurations, yml is more readable. However, the most important thing is to match the format already used in the existing project. If both exist, properties takes priority.
Loading a Single Configuration Value with @Value
The simplest way to use a configuration file’s value in Java code is the @Value annotation.
# application.properties
app.name=MySpringBootApp
app.timeout=30
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
@Component
public class AppConfig {
@Value("${app.name}")
private String appName;
@Value("${app.timeout:30}") // Uses 30 if undefined
private int timeout;
}
@Value is convenient for loading a single configuration value, but when there are many related settings, @ConfigurationProperties, covered next, is more suitable.
Grouping Related Settings with @ConfigurationProperties
@ConfigurationProperties is a Spring Boot annotation that binds multiple related configuration values together into a single type-safe class.
When there are multiple related properties, grouping them with @ConfigurationProperties makes them type-safe and easier to manage.
# application.yml
app:
database:
host: localhost
port: 3306
@ConfigurationProperties(prefix = "app.database")
public record DatabaseProperties(String host, int port) {}
Enable it in your main class to use it.
@SpringBootApplication
@EnableConfigurationProperties(DatabaseProperties.class)
public class MyApplication { ... }
@Service
public class DatabaseService {
private final DatabaseProperties props;
// Can be injected and used via the constructor
}
@Value is sufficient for a single configuration value, but @ConfigurationProperties is more appropriate for a group of related settings.
Validating @ConfigurationProperties Values
To cut to the conclusion: to validate configuration properties in Spring Boot, you simply need to add @Validated to the target class. Let’s look at the specific steps.
Mistakes in configuration values, if left unaddressed, will surface as runtime errors after startup. By adding @Validated to a @ConfigurationProperties class, you can validate configuration values at startup using Bean Validation (jakarta.validation) annotations — if there’s an invalid value, the application won’t start (fail-fast).
First, add spring-boot-starter-validation as a dependency.
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-validation</artifactId>
</dependency>
Add constraint annotations to the properties you want to validate.
import jakarta.validation.constraints.Max;
import jakarta.validation.constraints.Min;
import jakarta.validation.constraints.NotBlank;
import org.springframework.validation.annotation.Validated;
@Validated
@ConfigurationProperties(prefix = "app.database")
public record DatabaseProperties(
@NotBlank String host,
@Min(1) @Max(65535) int port
) {}
If a value violates a constraint, a BindValidationException is thrown at startup, and the log will show which property violated which constraint. Since this lets you catch configuration mistakes at the moment of startup rather than after going live in production, it’s recommended to pair @ConfigurationProperties with validation whenever you use it.
Note that this mechanism does not apply to @Value. Settings that need validation should be moved to @ConfigurationProperties.
Binding Lists and Maps, and IDE Autocompletion
@ConfigurationProperties can bind not only simple values but also List and Map directly.
app:
servers:
- host1.example.com
- host2.example.com
timeouts:
connect: 5
read: 30
@ConfigurationProperties(prefix = "app")
public record AppProperties(
List<String> servers,
Map<String, Integer> timeouts
) {}
As you accumulate more properties classes, instead of listing each one individually in @EnableConfigurationProperties, you can add @ConfigurationPropertiesScan to your main class to scan the entire package, making management easier.
Also, adding spring-boot-configuration-processor as a dependency (annotationProcessor) generates metadata at build time, enabling autocompletion and description display for your custom properties in the IDE. This is effective for preventing configuration mistakes in team development.
Overriding Production Settings with Environment Variables
Sensitive information such as passwords should be managed via environment variables. By converting dots and hyphens in the property name to underscores and lowercase letters to uppercase, the value is loaded automatically.
For example, spring.datasource.password can be overridden by the environment variable SPRING_DATASOURCE_PASSWORD.
spring:
datasource:
password: ${DB_PASSWORD:defaultpassword}
export DB_PASSWORD=super-secure-password
java -jar myapp.jar
In addition to overriding with environment variables, if you want to safely store encrypted sensitive values directly in the configuration file itself, refer to “How to Encrypt Sensitive Information in Configuration Files Using Jasypt in Spring Boot”. For configurations that inject settings from a ConfigMap or Secret on Kubernetes, see “How to Deploy a Spring Boot App to Kubernetes”.
Environment Variable Name Conversion Rules (Relaxed Binding)
The mechanism by which Spring Boot maps environment variables to properties is called relaxed binding. The conversion rules for environment variable names follow these 3 steps:
- Replace dots (
.) with underscores (_) - Remove hyphens (
-) - Convert everything to uppercase
Here are some conversion examples.
| Property Name | Environment Variable Name |
|---|---|
spring.datasource.password | SPRING_DATASOURCE_PASSWORD |
app.base-url | APP_BASEURL |
server.servlet.context-path | SERVER_SERVLET_CONTEXTPATH |
Note that hyphens are “removed,” not “converted to underscores.” Writing APP_BASE_URL would be interpreted as app.base.url, and would not bind to app.base-url. If an environment variable isn’t taking effect, this conversion rule is the first thing to suspect — it often resolves the issue quickly.
Reusing Configuration Values with Placeholders
You can reference other property values using the ${} placeholder.
app.base-url=https://api.example.com
app.endpoint.users=${app.base-url}/users
# Uses the default value if the environment variable is undefined
server.port=${SERVER_PORT:8080}
Understanding Property Priority
When loading settings from multiple sources, the priority order is as follows (higher items take precedence):
- Command-line arguments
- JVM system properties (
-Doption) - OS environment variables
- application-{profile}.properties/yml
- application.properties/yml
JVM system properties are values passed with the -D option, as in java -Dapp.name=foo -jar myapp.jar. You can retrieve them directly in Java with System.getProperty("app.name"), but they can also be referenced from Spring Boot’s Environment or @Value using the same ${app.name} placeholder. Since they take precedence over OS environment variables, they’re useful for temporary local overrides.
java -Dapp.name=local-override -jar myapp.jar
To switch settings per environment, use Profiles.
java -jar myapp.jar --spring.profiles.active=prod
For details on Profiles, see “How to Safely Switch Environment-Specific Configuration Using Spring Boot Profiles”.
Common Pitfalls
Here are some errors commonly encountered with configuration files.
Property not loading
If ${key} appears as-is instead of being resolved with @Value, there may be a typo in the key name, or the class may be missing the @Component annotation. Also check that the configuration file is located in src/main/resources.
YAML indentation errors
YAML does not allow tab characters. Use 2 spaces consistently for indentation. Using your IDE’s YAML support can help you catch errors before they occur.
Environment variable not taking effect
The environment variable name must be converted from spring.datasource.password to SPRING_DATASOURCE_PASSWORD — dots and hyphens become underscores, and lowercase letters become uppercase.
Frequently Asked Questions (FAQ)
Q. What is application.properties?
A. It’s a configuration file for defining the behavior of a Spring Boot application (server port, database connection information, log levels, etc.) outside of the code. When placed in src/main/resources, it’s automatically loaded at startup, allowing you to switch behavior per environment without changing the code.
Q. Should I use application.properties or application.yml?
A. Since there’s almost no functional difference, the most important thing is consistency within your team or project. If you have many deeply nested settings, YAML is more readable; for flat, small configurations, properties is sufficient. If both exist, the values in application.properties take priority.
Q. What’s the difference between @Value and @ConfigurationProperties?
A. @Value injects a single configuration value into one field at a time, whereas @ConfigurationProperties binds related settings sharing the same prefix into a single type-safe class. Only @ConfigurationProperties supports startup validation via @Validated and metadata generation for IDE autocompletion, so it’s standard practice to choose @ConfigurationProperties whenever there are two or more related settings.
Q. If both an environment variable and a configuration file value exist, which takes priority?
A. OS environment variables take priority over configuration file values. The overall priority order is “command-line arguments > OS environment variables > profile-specific configuration files > application.properties/yml.” This mechanism is why overriding passwords via environment variables works in production environments.
Summary
Spring Boot’s configuration management follows a simple mechanism: externalize settings into properties or yml files, and load them using @Value or @ConfigurationProperties.
In production, sensitive information such as passwords is overridden with environment variables, and Profiles are used to switch settings per environment. Understanding property priority helps prevent unintended overrides of your settings.
Keep the following in mind for real-world use:
- Manage sensitive information via environment variables, not in configuration files
- Group related settings using
@ConfigurationProperties - Standardize the configuration file format within your team
Mastering the basics of configuration management lets you build applications that can flexibly adapt to differences between environments.