00:00

application.properties vs YAML in Spring Boot

Last updated:

Every Spring Boot application reads its settings, such as the server port, database URL or the timeout for a payment gateway, from a configuration file. Spring Boot supports two formats for that file, the classic application.properties and application.yml written in YAML, so the application.properties vs YAML question comes up in almost every new project. Both feed the same settings into your application, but they look different, fail in different ways and suit different teams.

This tutorial puts the two formats side by side for values, lists, maps, placeholders, multi-line text and profiles. It shows how Spring Boot loads both and which file wins when both exist, with every example checked on Spring Boot 4.1. It ends with the pitfalls that break real applications, how to test your configuration, and a clear recommendation for choosing a format.

1. What Are application.properties and application.yml?

Both are plain text files, usually in src/main/resources, that hold your application's settings as keys and values. A key is a dotted name such as shop.payment.timeout, and the value is the setting, such as 5s.

  • application.properties uses the classic Java properties format: one key=value pair per line, with the full key written out every time.
  • application.yml (or application.yaml) uses YAML, short for "YAML Ain't Markup Language". It writes the same keys as a tree, where indentation shows that one key is nested inside another.

Here is the same setting in both formats:

shop.payment.timeout=5s

Spring Boot turns both into the same key, shop.payment.timeout. Your Java code never knows which file the value came from.

2. Why the Format Choice Matters

Because both formats end up as the same keys, the choice is about the people and tools that read and edit the file:

  • Readability. YAML removes repeated prefixes, so a large configuration is shorter and its structure is easier to see.
  • Safety. The properties format is flat and forgiving. YAML depends on exact indentation and converts some unquoted values to other types, which causes bugs you will meet in section 8.
  • Tool support. @PropertySource and @TestPropertySource can load only properties files. Kubernetes manifests, Docker Compose files and CI pipelines are usually YAML, so teams that live in those tools often prefer it.
  • Consistency. Mixing both formats in one project makes it hard to know where a value is defined.

3. How Spring Boot Loads Both Formats

At startup Spring Boot looks for files named application in a set of default locations: the classpath root, a config folder on the classpath, the current working directory and a config folder (and its subfolders) next to it. For each file it finds, it picks a property source loader by file extension:

  • PropertiesPropertySourceLoader reads .properties (and .xml) files.
  • YamlPropertySourceLoader reads .yml and .yaml files using the SnakeYAML library.

The YAML loader flattens the tree into the same dotted keys a properties file would have, and list items become indexed keys such as shop.payment.currencies[0]. Each loaded file becomes a property source, a named group of key/value pairs, inside the Spring Environment. From there, @ConfigurationProperties classes and @Value fields read values without caring which format they came from. Many of the keys are also read by Spring Boot itself, which is how auto-configuration picks up settings such as server.port.

How Spring Boot loads properties and YAML filesapplication.properties is read by PropertiesPropertySourceLoader and application.yml by YamlPropertySourceLoader, which uses SnakeYAML and flattens the YAML tree. Both produce the same flat keys, such as shop.payment.retry.max-attempts and shop.payment.currencies[0], in the Spring Environment. @ConfigurationProperties records and @Value fields read those keys without knowing which file they came from. FilesLoadersEnvironment (flat keys)Your code application.properties retry.max-attempts=3 application.yml retry: max-attempts: 3 PropertiesPropertySourceLoader reads key=value lines YamlPropertySourceLoader SnakeYAML, then flattens the tree into keys shop.payment.provider shop.payment.timeout shop.payment.retry.max-attempts shop.payment.currencies[0] shop.payment.currencies[1] shop.payment.fee-rates.card ... @ConfigurationProperties typed record @Value field or parameter both produce the same keys
Figure 1: each format has its own loader, but both end up as the same flat keys in the Environment, which is all your code ever sees.

4. Setup

There is nothing to install. Every Spring Boot project supports both formats, because spring-boot-starter (pulled in by every other starter) already includes SnakeYAML. To use YAML, rename application.properties to application.yml and rewrite its contents. Spring Initializr creates application.properties by default.

Two optional dependencies make configuration safer. The validation starter lets you reject bad values at startup, and the configuration processor generates metadata that gives you auto-completion for your own keys in both file types in IntelliJ IDEA and VS Code:

pom.xml
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-validation</artifactId>
</dependency>
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-configuration-processor</artifactId>
    <optional>true</optional>
</dependency>

5. Properties vs YAML Syntax Side by Side

Each tabbed block below shows the same configuration in both formats. Spring Boot loads every pair into exactly the same keys.

5.1 Simple Values and Nested Keys

spring.application.name=shop
shop.payment.provider=sandbox
shop.payment.timeout=5s
shop.payment.retry.max-attempts=3
shop.payment.retry.backoff=500ms
  • YAML separates a key from its value with a colon and a space (key: value). The space is required.
  • Nesting is done with spaces only. Two spaces per level is the common style. Tabs are not allowed.
  • Kebab case (max-attempts) is the recommended key style in both formats. Spring Boot's relaxed binding still maps it to a Java field named maxAttempts.

5.2 Lists

Lists use indexes in properties and dashes in YAML. A list of objects, such as webhooks, repeats the index for each field in properties, while YAML groups the fields of each item under one dash:

shop.payment.currencies[0]=INR
shop.payment.currencies[1]=USD
shop.payment.currencies[2]=EUR
shop.payment.webhooks[0].event=payment.captured
shop.payment.webhooks[0].url=https://shop.example.com/hooks/payment-captured
shop.payment.webhooks[1].event=payment.failed
shop.payment.webhooks[1].url=https://shop.example.com/hooks/payment-failed

For a simple list, properties also accepts a comma-separated value: shop.payment.currencies=INR,USD,EUR binds to the same List<String>.

5.3 Maps and Keys with Special Characters

A map binds every child key under a prefix. If a map key contains a dot, a slash or another special character, wrap it in square brackets. In YAML the bracketed key must also be in quotes, because [ has its own meaning in YAML:

shop.payment.fee-rates.card=1.8
shop.payment.fee-rates.upi=0.0
shop.payment.fee-rates.[net.banking]=0.9

Without the brackets, the binder treats the dot as another nesting level, and the entry is silently missing from the map. Section 8.6 shows the result.

5.4 Placeholders and Default Values

Both formats support ${...} placeholders. A placeholder is replaced with the value of another key or an environment variable, and the part after the colon is a default used when nothing else is set:

shop.payment.base-url=${PAYMENT_BASE_URL:https://sandbox.payments.example.com}

5.5 Multi-Line Values

YAML has two block styles for long text. | keeps the line breaks, and > folds the lines into one, joined with spaces. Properties has a single option: a backslash at the end of a line continues the value on the next line, and the leading spaces of that next line are dropped.

shop.payment.failure-message=Payment failed. \
    Please retry or choose another method.

The YAML version produces Payment failed. Please retry or choose another method. followed by a line break, because both block styles end the value with a newline unless you write >- or |-. The properties version produces the same sentence with no newline at the end.

5.6 Multi-Document Files and Profiles

A profile is a named set of settings, such as dev or prod, that you switch on with spring.profiles.active. You can put profile settings in separate files (application-prod.yml) or in one multi-document file. YAML separates documents with ---. Properties files use a special comment, #--- (or !---) on a line of its own, with no leading spaces:

shop.payment.provider=sandbox
shop.payment.timeout=5s
#---
spring.config.activate.on-profile=prod
shop.payment.provider=razorpay
shop.payment.timeout=3s

The second document is used only when the prod profile is active, and its values override the first document. The Spring Boot profiles tutorial covers profile groups and activation in detail.

6. application.properties vs application.yml: Comparison Table

Featureapplication.propertiesapplication.yml
StructureFlat, full key on every lineTree, nesting by indentation
Repeated prefixesYesNo
Listskey[0]=value or comma-separated- value items
Multi-line textBackslash continuation only| (keep lines) and > (fold lines)
Multi-document file#--- or !------
Values are always stringsYesNo, unquoted values can change type (section 8.2)
Whitespace sensitiveNo (but trailing spaces are kept)Yes, and tabs are rejected
Default file encodingISO-8859-1UTF-8
@PropertySource / @TestPropertySourceSupportedNot supported
Same folder as the other formatWinsLoses

7. Configuration Precedence: Which Value Wins?

When the same key is set in more than one place, Spring Boot uses the source with the higher precedence. The rules that matter most day to day, from lowest to highest:

Configuration precedence from lowest to highestSeven sources stacked from lowest precedence at the bottom to highest at the top: application files inside the jar, where application.properties beats application.yml in the same folder; profile-specific files inside the jar; application files outside the jar; profile-specific files outside the jar; OS environment variables; Java system properties; and command-line arguments at the top. Command-line arguments --shop.payment.timeout=10s Java system properties -Dshop.payment.timeout=10s OS environment variables SHOP_PAYMENT_TIMEOUT=10s Profile-specific files outside the jar ./config/application-prod.yml Application files outside the jar ./config/application.yml Profile-specific files inside the jar application-prod.yml Application files inside the jar application.properties beats application.yml higher precedence wins
Figure 2: the main configuration sources, lowest at the bottom. A value from a higher source replaces the same key from any source below it.
  • A profile-specific file (application-prod.yml) overrides the plain file.
  • A file outside the jar (for example in ./config) overrides the same file packaged inside it.
  • Environment variables, then Java system properties (-D), then command-line arguments (--key=value) override every file.
  • In a multi-document file, a later document overrides an earlier one when it is active.
  • If application.properties and application.yml sit in the same folder, both are loaded, and for any key set in both, the .properties value wins.

That last rule surprises people. If you rename application.properties to application.yml but an old properties file remains on the classpath, its values silently override your new YAML. When you switch formats, delete the old file.

8. Common YAML Pitfalls and How to Fix Them

Every example in this section was run on Spring Boot 4.1.

8.1 Tabs in Indentation

YAML allows spaces only. One tab character stops the application at startup:

Console
ERROR o.s.boot.SpringApplication : Application run failed
while scanning for the next token
found character '\t(TAB)' that cannot start any token. (Do not use \t(TAB) for indentation)
 in 'reader', line 11, column 1:
    	  max-attempts: 3
    ^

How to fix it: configure your editor to insert spaces in .yml files and to show whitespace characters.

8.2 Unquoted Values That Change Type

Spring Boot's YAML parser, SnakeYAML, follows YAML 1.1 rules, which give some unquoted values a special meaning. When such a value is bound to a String, you get the converted text, not what you typed:

You writeYAML reads it asYour String field receivesSame value in .properties
provider: NOBoolean false (also yes, on, off)falseNO
merchant-code: 0123Octal number 83830123
api-version: 1.10Decimal number 1.11.11.10

How to fix it: put quotes around any value that must stay text exactly as written, such as country codes, codes with leading zeros, version numbers and phone numbers: merchant-code: "0123".

8.3 Special Characters at the Start of a Value

Characters such as @, `, *, &, !, %, { and [ have a meaning at the start of a YAML value, and : or # inside a value end it early. An unquoted value that starts with @ fails like this:

Console
ERROR o.s.boot.SpringApplication : Application run failed
while scanning for the next token
found character '@' that cannot start any token. (Do not use @ for indentation)
 in 'reader', line 26, column 20:
        support-email: @payments-team
                       ^

How to fix it: quote the value, for example support-email: "@payments-team". Use double quotes if you need escapes such as \n.

8.4 Duplicate Keys

Spring Boot rejects a YAML file that sets the same key twice at the same level. In a properties file, the last line silently wins.

Console
ERROR o.s.boot.SpringApplication : Application run failed
while constructing a mapping
 in 'reader', line 7, column 5:
        provider: sandbox
        ^
found duplicate key timeout
 in 'reader', line 26, column 5:
        timeout: 8s
        ^

8.5 A Key at the Wrong Indentation Level

If a key is valid YAML but indented under the wrong parent, it becomes a different key, and nothing binds to it. Here max-attempts slipped out of retry:

application.yml (wrong)
shop:
  payment:
    retry:
      backoff: 500ms
    max-attempts: 3   # one level too far left

Spring Boot ignores the unknown key shop.payment.max-attempts, so a retry.maxAttempts field would quietly be 0. If the settings bind to a @Validated @ConfigurationProperties record with @Min(1) on that field, startup fails with a clear error instead:

Console
***************************
APPLICATION FAILED TO START
***************************

Description:

Binding to target com.javabytechie.shop.payment.PaymentProperties failed:

    Property: shop.payment.retry.maxAttempts
    Value: "0"
    Reason: must be greater than or equal to 1

8.6 Map Keys with Dots or Slashes

Without brackets, a map key such as net.banking is read as two nesting levels and the entry disappears from the map. A slash is stripped, so upi/intent becomes upiintent. This happens in both formats:

application.yml (wrong) and Console
# application.yml
    fee-rates:
      card: 1.8
      upi: 0.0
      net.banking: 0.9
      upi/intent: 0.2

# Bound Map<String, BigDecimal>
{card=1.8, upi=0.0, upiintent=0.2}

How to fix it: write "[net.banking]": 0.9 in YAML or fee-rates.[net.banking]=0.9 in properties, as in section 5.3.

8.7 Lists Are Replaced, Not Merged

Redefining a list in a profile replaces the whole list. If the base document lists INR, USD and EUR and the prod document lists only INR, the prod profile gets [INR]. If prod needs one extra currency, it must list all of them. Single values that prod doesn't redefine are still inherited from the base document. The same rules apply in properties files.

9. Common application.properties Pitfalls

9.1 Non-ASCII Characters

Spring Boot reads .properties files with the ISO-8859-1 character set, while YAML files are read as UTF-8. A UTF-8 file with shop.payment.note=Café Mocha – ₹499 comes out garbled:

Console
shop.payment.note = Café Mocha â â¹499

How to fix it: write such characters as Unicode escapes (Café Mocha – ₹499), keep that text in a YAML file, or put user-facing text in a message bundle instead of the configuration file.

Version note: Spring Boot 4.1 added an encoding option for imported files, for example spring.config.import=classpath:messages.properties[encoding=utf-8]. It applies only to files loaded through spring.config.import. ISO-8859-1 remains the default, and earlier versions don't have the option.

9.2 Trailing Whitespace

A properties value keeps any spaces after it. shop.payment.provider=sandbox followed by three invisible spaces binds as "sandbox   ", which can break comparisons and URLs. How to fix it: set your editor to trim trailing whitespace, and validate values that must match a fixed list.

10. Testing Your Configuration

A configuration test proves that your files bind the way you expect, especially profile overrides. The test below loads the multi-document file from section 5.6 (in either format) twice and compares the values before and after each override:

PaymentConfigTest.java
package com.javabytechie.shop.payment;

import static org.assertj.core.api.Assertions.assertThat;

import org.junit.jupiter.api.Test;
import org.springframework.boot.SpringBootConfiguration;
import org.springframework.boot.WebApplicationType;
import org.springframework.boot.builder.SpringApplicationBuilder;
import org.springframework.context.ConfigurableApplicationContext;

class PaymentConfigTest {

    // An empty configuration: only the config files are loaded, no beans are scanned
    @SpringBootConfiguration
    static class ConfigOnly {
    }

    private ConfigurableApplicationContext start(String... args) {
        return new SpringApplicationBuilder(ConfigOnly.class)
                .web(WebApplicationType.NONE)
                .run(args);
    }

    @Test
    void prodProfileOverridesTheBaseDocument() {
        try (var base = start(); var prod = start("--spring.profiles.active=prod")) {
            assertThat(base.getEnvironment().getProperty("shop.payment.provider")).isEqualTo("sandbox");
            assertThat(base.getEnvironment().getProperty("shop.payment.timeout")).isEqualTo("5s");

            assertThat(prod.getEnvironment().getProperty("shop.payment.provider")).isEqualTo("razorpay");
            assertThat(prod.getEnvironment().getProperty("shop.payment.timeout")).isEqualTo("3s");
        }
    }

    @Test
    void commandLineArgumentBeatsTheConfigFile() {
        try (var fromFile = start(); var fromArgs = start("--shop.payment.timeout=10s")) {
            assertThat(fromFile.getEnvironment().getProperty("shop.payment.timeout")).isEqualTo("5s");
            assertThat(fromArgs.getEnvironment().getProperty("shop.payment.timeout")).isEqualTo("10s");
        }
    }
}
  • The empty @SpringBootConfiguration class makes Spring Boot load the configuration files without scanning or creating any of your beans, so the test is fast.
  • Each try block closes both contexts, so the test leaves nothing running.
  • Command-line arguments have high precedence, so --shop.payment.timeout=10s overrides the file without editing it.
  • The test needs only spring-boot-starter-test, which brings JUnit 5 and AssertJ.

Inside @SpringBootTest classes, use the properties attribute (@SpringBootTest(properties = "shop.payment.timeout=10s")) or @ActiveProfiles("prod") to change settings. Remember that @TestPropertySource loads only properties files, never YAML. The Spring Boot testing tutorial covers the test annotations in more detail.

11. When to Use application.properties and When to Use YAML

Choose application.properties whenChoose application.yml when
The configuration is small and flatThe configuration is large and deeply nested
You load extra files with @PropertySource or @TestPropertySourceYou have many lists, maps or lists of objects
Values include codes, versions or flags you never want convertedYou need readable multi-line text
People who rarely edit YAML change the fileThe team already works in YAML (Kubernetes, CI, Docker Compose)
You want copy-pasteable, greppable full keysYou want to see the whole structure at a glance

Neither format is faster or more powerful for Spring Boot itself. Pick one per project and use it everywhere.

12. Best Practices

  • Use one format per project, and delete the old file when you switch, so a forgotten application.properties can't override your YAML.
  • Bind settings to @ConfigurationProperties records instead of scattering @Value fields, so every group of settings has one typed home. The configuration properties tutorial shows how.
  • Validate configuration at startup with @Validated and Bean Validation, so typos and wrong indentation fail fast instead of producing silent defaults.
  • Quote YAML values that must stay text, such as codes with leading zeros, version numbers, country codes and anything starting with a special character.
  • Use square brackets for map keys that contain dots, slashes or other special characters.
  • Write keys in kebab case (max-attempts) in both formats and let relaxed binding map them to Java names.
  • Use Duration and DataSize types with units such as 5s or 10MB instead of bare numbers that need a comment to explain them.
  • Keep secrets out of both files, and supply them through environment variables or a secrets manager with placeholders such as ${PAYMENT_API_KEY}.
  • Set your editor to spaces and trimmed whitespace, which prevents tab errors in YAML and hidden trailing spaces in properties.
  • Add spring-boot-configuration-processor so your own keys get auto-completion and documentation in the IDE.
  • Test profile overrides with a small configuration test, checking the value before and after the override.

13. Summary

In the application.properties vs YAML choice, both formats load into the same flat keys in the Spring Environment, so your code works the same way with either. Properties files are flat, literal and supported everywhere, including @PropertySource. YAML is shorter and easier to read for nested settings, lists and multi-line text, but it is sensitive to indentation and converts some unquoted values. Whichever you choose, bind settings to validated @ConfigurationProperties records, use brackets for special map keys, remember that lists are replaced and not merged, and test your profile overrides. The main recommendation: use YAML for larger, nested configurations and quote values that must stay text, use properties for small, flat ones, and never keep both formats side by side.