HomeSpring BootSpring Boot application.yml: Syntax, Profiles and Pitfalls
Spring Boot

Spring Boot application.yml: Syntax, Profiles and Pitfalls

Spring Boot application.yml guide: YAML syntax, profiles with on-profile, lists and maps, config locations, precedence, and common YAML pitfalls, with examples.

application.yml in 30 Seconds

application.yml is Spring Boot's configuration file in YAML format. It sets the same properties as application.properties, but nests them by indentation instead of repeating prefixes: spring.datasource.url=... becomes a datasource: url: ... block under spring:. The essentials: - Put it in src/main/resources (the file is application.yml or application.yaml). - Indent with spaces, never tabs. Two spaces per level is the convention. - A key and its value are separated by a colon and a space (key: value). - Environment-specific values go in application-{profile}.yml, or in extra documents separated by --- with spring.config.activate.on-profile. - Use ${NAME:default} to read environment variables and keep secrets out of the file. - Quote values that could be misread as something else: "yes", "NO", "1.10", "0755", "%d{HH:mm}", "p#ss". If both application.properties and application.yml exist in the same location, both are loaded and .properties wins for keys defined in both.

YAML vs Properties

Both application.properties and application.yml configure the same Spring Boot auto-configuration. Both files are flattened into the same property model, so a setting means the same thing in either format. The choice is mostly about readability and team preference. YAML's advantages: hierarchical structure eliminates repeated prefixes, lists and maps have natural syntax, multi-line strings are clean, and several profile configurations can live in one file. Properties' advantages: simpler syntax with no indentation rules, universally familiar, easy to append single lines, and no risk of an indentation mistake silently moving a key. Differences that matter in practice: - YAML files cannot be loaded with @PropertySource or @TestPropertySource. Use spring.config.import for extra YAML files, or @SpringBootTest(properties = ...). - YAML scalars are typed by the parser (booleans, numbers, null), so unquoted values can change type. See the type-surprises section. - Profile-specific documents (--- plus spring.config.activate.on-profile) exist only in multi-document files. - A tab character or wrong indentation is a syntax error or, worse, a valid but different structure. The key YAML rule: indentation must be consistent spaces (never tabs). A single wrong indentation can change a property key without any error, and the application then silently runs with defaults.

YAML Syntax Fundamentals

Understanding YAML syntax rules prevents the indentation errors and type coercion surprises that are the most common YAML pitfalls. The example below is one valid file, so you can paste it as it is. Note that each key (server, app, spring, logging) appears exactly once at the top level: repeating a key is an error, not a merge. Spring Boot also applies relaxed binding: allowed-origins, allowedOrigins and ALLOWED_ORIGINS all bind to the same Java property, and kebab-case is the recommended style in YAML. Durations (30s, 5m) and data sizes (10MB) are understood by properties typed as Duration and DataSize.
yaml
# ── Key-value pairs and nesting ──────────────────────────────────────
server:
  port: 8080                # integer — no quotes needed
  servlet:
    context-path: /api      # string — no quotes needed

# ── Strings, numbers, booleans ───────────────────────────────────────
app:
  name: MyApplication                # unquoted — fine for simple strings
  description: "My Spring Boot App: v2"  # quote values with ": " or " #" inside
  message: 'It''s working'           # single quotes — write '' for a literal '
  url: http://localhost:8080         # a colon NOT followed by a space is fine unquoted
  multiline: |                       # literal block — preserves newlines
    Line one
    Line two
  folded: >                          # folded block — newlines become spaces
    This is a very long
    description that wraps
  jwt:
    expiration: 86400                # int
    rate: 0.15                       # float
    enabled: true                    # boolean — always write true/false
    timeout: 30s                     # Duration, when bound to a Duration property
    max-upload: 10MB                 # DataSize, when bound to a DataSize property

  # ── Lists ──────────────────────────────────────────────────────────
  allowed-origins:                   # block list
    - https://myapp.com
    - https://admin.myapp.com
  allowed-methods: [GET, POST, PUT]  # inline list

  # ── Maps ───────────────────────────────────────────────────────────
  external-services:
    inventory: https://inventory:8082
    pricing: https://pricing:8083

# ── Placeholders: environment variables and defaults ─────────────────
spring:
  datasource:
    username: ${DB_USERNAME:app}                                  # with default
    password: ${DB_PASSWORD}                                      # required: startup fails if it is not defined
    url: ${DATABASE_URL:jdbc:mysql://localhost:3306/mydb}         # the default may contain colons
  profiles:
    active: dev                      # or a list: [dev, metrics]

# ── Logging levels are a map ─────────────────────────────────────────
logging:
  level:
    root: INFO
    com.example: DEBUG
    org.springframework.web: INFO

Where Spring Boot Looks for application.yml and What Wins

Spring Boot searches for application.yml (and application.properties, and the .yaml extension) in these locations. Later locations override earlier ones: 1. the classpath root 2. a /config package on the classpath 3. the current directory (next to the jar) 4. a /config subdirectory of the current directory 5. immediate subdirectories of /config in the current directory Profile-specific files (application-prod.yml) override the base file, and external sources override both. Higher number wins in this simplified order, from lowest to highest priority: 1. application.yml / application.properties inside the jar 2. application.yml / application.properties outside the jar 3. profile-specific files (inside, then outside the jar) 4. OS environment variables (SPRING_DATASOURCE_URL, SERVER_PORT) 5. Java system properties (-Dserver.port=9000) 6. command-line arguments (--server.port=9000) This is why a container can override anything in the packaged file with an environment variable, without a placeholder: SPRING_DATASOURCE_URL sets spring.datasource.url, and SPRING_PROFILES_ACTIVE=prod activates a profile. Lists can be given as comma-separated values (APP_ALLOWED_ORIGINS=a,b). To change the lookup, use spring.config.name (other file name), spring.config.location (replaces the default locations), spring.config.additional-location (adds locations) and spring.config.import to pull in extra files, for example optional:file:./secrets.yml. The optional: prefix prevents a startup failure when the file is missing. The full order of all property sources is in the Spring Boot reference documentation under Externalized Configuration.
yaml
# Extra locations and imports
# application.yml
spring:
  config:
    import:
      - optional:file:./local-overrides.yml        # ignored if the file does not exist
      - optional:configtree:/run/secrets/          # one file per property, e.g. Docker/Kubernetes secrets

# Command line:
#   java -jar app.jar --spring.config.additional-location=file:/etc/myapp/
#   java -jar app.jar --spring.config.location=classpath:/,file:/etc/myapp/
#   java -jar app.jar --spring.profiles.active=prod --server.port=9000

# Environment variables (relaxed binding):
#   SPRING_PROFILES_ACTIVE=prod
#   SPRING_DATASOURCE_URL=jdbc:postgresql://db:5432/app
#   SPRING_DATASOURCE_HIKARI_MAXIMUM_POOL_SIZE=20
#   APP_ALLOWED_ORIGINS=https://a.com,https://b.com

Complete application.yml

A complete application.yml for a REST API with JPA, security, Redis and Actuator, written for Spring Boot 3.x. Compare it with the equivalent flat .properties file to see how much repetition YAML removes. Secrets come from environment variables, DEBUG logging is not enabled by default, and deprecated property names have been replaced (for example server.tomcat.threads.max instead of server.tomcat.max-threads, and logging.logback.rollingpolicy.* instead of logging.file.max-size). Two notes for other versions: Spring Boot 2.x uses spring.redis.* instead of spring.data.redis.*, and you normally do not need to set the Hibernate dialect, because Hibernate detects it from the database (an explicit MySQL8Dialect is deprecated in Hibernate 6).
yaml
# application.yml — complete Spring Boot 3.x configuration

server:
  port: 8080
  servlet:
    context-path: /api
  tomcat:
    threads:
      max: 200
      min-spare: 10
    connection-timeout: 5s
  compression:
    enabled: true
    mime-types: application/json,text/html
    min-response-size: 2KB
  shutdown: graceful

spring:
  application:
    name: myapp

  datasource:
    url: ${DATABASE_URL:jdbc:mysql://localhost:3306/mydb?useSSL=false&serverTimezone=UTC}
    username: ${DB_USERNAME:app}
    password: ${DB_PASSWORD}
    hikari:
      pool-name: myapp-pool
      maximum-pool-size: 10
      minimum-idle: 5
      idle-timeout: 300000
      connection-timeout: 20000
      max-lifetime: 1200000
      keepalive-time: 60000

  jpa:
    hibernate:
      ddl-auto: validate
    show-sql: false
    open-in-view: false
    properties:
      hibernate:
        format_sql: true
        default_batch_fetch_size: 16
        jdbc:
          batch_size: 25

  flyway:
    enabled: true
    locations: classpath:db/migration
    validate-on-migrate: true

  security:
    oauth2:
      resourceserver:
        jwt:
          issuer-uri: https://auth.example.com

  data:
    redis:                      # Spring Boot 2.x: spring.redis
      host: ${REDIS_HOST:localhost}
      port: 6379
      timeout: 2s

  cache:
    type: redis

  jackson:
    serialization:
      write-dates-as-timestamps: false
    default-property-inclusion: non_null

  servlet:
    multipart:
      max-file-size: 10MB
      max-request-size: 50MB

  lifecycle:
    timeout-per-shutdown-phase: 30s

management:
  endpoints:
    web:
      exposure:
        include: health,info,metrics,loggers
  endpoint:
    health:
      show-details: when-authorized
      show-components: always
  info:
    env:
      enabled: true
    java:
      enabled: true

logging:
  level:
    root: INFO
    com.example: INFO
  pattern:
    console: "%d{HH:mm:ss.SSS} %-5level [%thread] %logger{36} - %msg%n"   # quotes required: the value starts with %
  file:
    name: logs/myapp.log
  logback:
    rollingpolicy:
      max-file-size: 50MB
      max-history: 30

app:
  jwt:
    secret: ${JWT_SECRET}
    expiration: 86400
    issuer: https://myapp.com
  mail:
    from: noreply@myapp.com
  features:
    payments:
      enabled: true
    notifications:
      enabled: true

info:
  app:
    name: My Application
    version: 2.1.0

Profile-Specific YAML — spring.config.activate.on-profile

Spring Boot supports two approaches for profile-specific YAML: separate files (application-dev.yml) or multi-document YAML with --- separators in a single file. Both are valid and can be combined. In a multi-document file, documents are processed from top to bottom and later documents override earlier ones. A document without an on-profile condition always applies. Use spring.config.activate.on-profile (Spring Boot 2.4 and later) to restrict a document to a profile. The older spring.profiles key used for this purpose before 2.4 is no longer supported for activation. The on-profile value is a profile expression: a single name (prod), a negation (!prod), an or (dev | test) or an and (prod & eu), wrapped in quotes when it contains special characters. Do not use spring.config.activate.on-profile inside a profile-specific file such as application-dev.yml, and do not set spring.profiles.active (or include) inside a profile-specific document; Spring Boot rejects both with an InvalidConfigDataPropertyException. Activate profiles from the base document, an environment variable or the command line. To activate several profiles at once, define a profile group in the base document: spring.profiles.group.prod: [proddb, prodmq] makes --spring.profiles.active=prod enable all three.
yaml
# ── APPROACH 1: Separate files ───────────────────────────────────────
# application.yml          — base config for all profiles
# application-dev.yml      — dev overrides (loaded when 'dev' is active)
# application-prod.yml     — prod overrides

# application-dev.yml:
spring:
  datasource:
    url: jdbc:h2:mem:devdb;MODE=MySQL
    username: sa
    password: ""
  h2:
    console:
      enabled: true
  jpa:
    show-sql: true
    hibernate:
      ddl-auto: create-drop

logging:
  level:
    com.example: DEBUG

# ── APPROACH 2: Multi-document YAML (single file) ─────────────────────
# Use --- to separate YAML documents within one file.
# Each document can target a profile with spring.config.activate.on-profile.

# application.yml:
---
# Document 1 — base config (no profile restriction, always applied):
server:
  port: 8080
spring:
  profiles:
    active: dev                          # default profile; override with SPRING_PROFILES_ACTIVE
    group:
      prod: [proddb, metrics]            # activating 'prod' also activates these
  jpa:
    show-sql: false
logging:
  level:
    root: INFO

---
# Document 2 — development overrides:
spring:
  config:
    activate:
      on-profile: dev                    # only applied when 'dev' is active
  datasource:
    url: jdbc:h2:mem:devdb
    username: sa
    password: ""
  jpa:
    show-sql: true
    hibernate:
      ddl-auto: create-drop
logging:
  level:
    com.example: DEBUG

---
# Document 3 — production settings:
spring:
  config:
    activate:
      on-profile: prod
  datasource:
    url: ${DATABASE_URL}
    username: ${DB_USERNAME}
    password: ${DB_PASSWORD}
    hikari:
      maximum-pool-size: 20
logging:
  level:
    root: WARN
    com.example: INFO

---
# Document 4 — everything except production (profile expression):
spring:
  config:
    activate:
      on-profile: "!prod"
management:
  endpoints:
    web:
      exposure:
        include: "*"

YAML Lists and Maps in Configuration

YAML's native list and map syntax makes complex configuration clean. The next section shows how Spring Boot binds these structures to Java types. Two behaviors to remember. First, lists are replaced, not merged: if a profile-specific file or a higher-priority source defines app.allowed-origins, its list completely replaces the list from the base file, item by item merging does not happen. Maps, in contrast, are merged key by key. Second, map keys that contain characters other than letters, digits, dots and dashes (a slash, for example) must be written in bracket notation, quoted: "[/api/users]": value.
yaml
# YAML lists — bound to Java List<String>:
app:
  allowed-origins:
    - https://myapp.com
    - https://admin.myapp.com
    - http://localhost:3000

  # Or inline list syntax:
  # allowed-origins: [https://myapp.com, https://admin.myapp.com]

  # YAML maps — bound to Java Map<String, String>:
  external-services:
    inventory: https://inventory:8082
    pricing: https://pricing:8083
    shipping: https://shipping:8084

  # Maps of typed sub-objects — bound to Map<String, ServiceConfig>:
  services:
    inventory:
      url: https://inventory:8082
      timeout: 5s
      retry-count: 3
    pricing:
      url: https://pricing:8083
      timeout: 3s
      retry-count: 2

  # List of objects — bound to List<NotificationChannel>:
  notification-channels:
    - type: email
      enabled: true
      priority: 1
    - type: sms
      enabled: true
      priority: 2
    - type: push
      enabled: false
      priority: 3

  # Map keys with special characters need bracket notation:
  route-roles:
    "[/api/users]": ADMIN
    "[/api/orders]": USER

Binding YAML to Java with @ConfigurationProperties

The YAML from the previous section binds to a typed class. Java records work as immutable configuration holders (constructor binding is automatic), names are matched with relaxed binding (allowed-origins maps to allowedOrigins), and Duration properties accept values such as 5s, 500ms or 2m. A plain number is read as milliseconds unless you set @DurationUnit. Register the class with @ConfigurationPropertiesScan (or @EnableConfigurationProperties) and add @Validated with Bean Validation annotations to fail fast at startup when a required value is missing. Add the spring-boot-configuration-processor dependency to get IDE autocompletion and documentation for your own properties in application.yml.
Java
@ConfigurationProperties(prefix = "app")
@Validated
public record AppProperties(
        @NotEmpty List<String> allowedOrigins,
        Map<String, String> externalServices,
        Map<String, ServiceConfig> services,
        List<NotificationChannel> notificationChannels,
        Map<String, String> routeRoles) {

    public record ServiceConfig(@NotBlank String url, Duration timeout, int retryCount) {}

    public record NotificationChannel(String type, boolean enabled, int priority) {}
}

@SpringBootApplication
@ConfigurationPropertiesScan          // registers AppProperties as a bean
public class Application {
    public static void main(String[] args) {
        SpringApplication.run(Application.class, args);
    }
}

@Service
public class InventoryClient {
    private final AppProperties.ServiceConfig config;

    public InventoryClient(AppProperties props) {
        this.config = props.services().get("inventory");   // url, timeout 5s, retryCount 3
    }
}

// pom.xml — IDE autocompletion for your own properties:
// <dependency>
//   <groupId>org.springframework.boot</groupId>
//   <artifactId>spring-boot-configuration-processor</artifactId>
//   <optional>true</optional>
// </dependency>

YAML Type Surprises: yes, NO, 1.10 and 0755

YAML scalars are typed by the parser before Spring Boot sees them. Spring Boot's YAML parser (SnakeYAML) follows YAML 1.1 rules, which produces some well-known surprises. When the target property is a String, the already converted value is turned back into a string, so you get "true", not what you typed. - yes, no, on, off (in any case) become booleans. A country code NO becomes false. Quote it. - A value that looks like a number loses formatting: 1.10 becomes 1.1, and 007 or 0755 are read as octal numbers. Quote version numbers and codes. - A bare key: with nothing after it is null. Write "" for an empty string. - An unquoted # starts a comment when it follows a space, so passwords containing " #" are cut off. Quote them. - Values starting with a special character must be quoted: %, *, &, !, |, >, @, {, [, and the backtick. A logging pattern starting with % always needs quotes. - Values with a colon followed by a space (": ") must be quoted. A colon in a URL (http://localhost:8080) is fine unquoted. - The key on is read as the boolean true. Quote keys that could be misread.
yaml
feature:
  enabled: yes            # boolean true (YAML 1.1) — write true
  country: NO             # boolean false!  — write "NO"
  switch: off             # boolean false
  version: 1.10           # float 1.1       — write "1.10"
  file-mode: 0755         # octal number 493 — write "0755"
  code: 007               # octal            — write "007"
  empty:                  # null (not an empty string)
  blank: ""               # empty string
  password: "p#ss word"   # quoted: '#' is safe
  pattern: "%d{HH:mm:ss}" # quoted: value starts with %
  wildcard: "*"           # quoted: '*' starts an alias in YAML
  note: "Build: 42"       # quoted: contains ': '
  url: http://localhost:8080   # fine: colon not followed by a space

YAML Pitfalls and How to Avoid Them

YAML's strict indentation rules and implicit type coercion cause subtle bugs that are hard to diagnose. These are the most common YAML mistakes in Spring Boot configuration. Each snippet below is a separate example, not one file. An important safety note: Spring Boot does not complain about keys it does not recognise. A typo or a wrongly indented key is usually ignored silently and the application runs with the default value. Always check the effective configuration (see the verification section).
yaml
# ── PITFALL 1: Tabs instead of spaces ────────────────────────────────
# WRONG — the next line starts with a TAB character; SnakeYAML throws a ScannerException:
server:
	port: 8080

# CORRECT — spaces only (2 spaces per level is Spring Boot convention):
server:
  port: 8080

# ── PITFALL 2: Wrong indentation level ───────────────────────────────
# WRONG — 'datasource' is at the top level, so the key becomes "datasource.url":
spring:
datasource:
  url: jdbc:mysql://...
# Symptom: "Failed to determine a suitable driver class" — Spring never saw your URL.

# CORRECT — key becomes "spring.datasource.url":
spring:
  datasource:
    url: jdbc:mysql://...

# ── PITFALL 3: Unquoted special characters ────────────────────────────
# WRONG — ": " inside a value:
app:
  description: My App: Version 2     # parse error: mapping values are not allowed here

# CORRECT:
app:
  description: "My App: Version 2"
  url: http://localhost:8080         # colon without a following space needs no quotes
  token: "abc #123"                  # a space followed by '#' starts a comment — quote it

# ── PITFALL 4: Booleans and other implicit types ─────────────────────
# yes/no/on/off are booleans in YAML 1.1 (see the type-surprises section):
feature:
  enabled: yes                       # true — write true / false explicitly

# ── PITFALL 5: Duplicate keys ────────────────────────────────────────
# WRONG — the same key twice at one level. Spring Boot's loader rejects duplicate
# keys and the application FAILS TO START with a "duplicate key" error:
spring:
  datasource:
    url: jdbc:h2:mem:devdb
  datasource:                        # duplicate key — startup error
    url: jdbc:mysql://...

# CORRECT — one 'datasource' key; use profiles to switch values.

# ── PITFALL 6: Multi-document separator ───────────────────────────────
# --- must be on its own line:
spring:
  application:
    name: myapp
---
spring:
  config:
    activate:
      on-profile: dev

# ── PITFALL 7: Profile rules in the wrong place ──────────────────────
# WRONG — activating profiles from inside a profile-specific document
# fails with InvalidConfigDataPropertyException:
---
spring:
  config:
    activate:
      on-profile: prod
  profiles:
    active: metrics                  # not allowed here

# ── PITFALL 8: Lists are replaced, not merged ────────────────────────
# application.yml:
app:
  allowed-origins:
    - https://myapp.com
    - https://admin.myapp.com
# application-dev.yml:
app:
  allowed-origins:
    - http://localhost:3000          # in 'dev' the list is ONLY localhost — the other two are gone

# ── PITFALL 9: Missing placeholder values ─────────────────────────────
spring:
  datasource:
    password: ${DB_PASSWORD}         # no default: startup fails with "Could not resolve placeholder"
    username: ${DB_USERNAME:app}     # with default: safe for local development

# ── PITFALL 10: @PropertySource and YAML ──────────────────────────────
# @PropertySource("classpath:extra.yml") does NOT parse YAML.
# Use spring.config.import: optional:classpath:extra.yml instead.

Properties vs YAML — Side by Side

A direct comparison showing when YAML's structure provides real value over flat properties, and when it does not.
yaml
# ── FLAT PROPERTIES (repetitive prefixes): ───────────────────────────

spring.datasource.url=jdbc:mysql://localhost:3306/mydb
spring.datasource.username=root
spring.datasource.password=secret
spring.datasource.hikari.maximum-pool-size=10
spring.datasource.hikari.minimum-idle=5
spring.datasource.hikari.connection-timeout=20000
spring.jpa.hibernate.ddl-auto=validate
spring.jpa.show-sql=false
spring.jpa.open-in-view=false
spring.jpa.properties.hibernate.format_sql=true
spring.jpa.properties.hibernate.default_batch_fetch_size=16

# ── EQUIVALENT YAML (hierarchical — no prefix repetition): ────────────

spring:
  datasource:
    url: jdbc:mysql://localhost:3306/mydb
    username: root
    password: secret
    hikari:
      maximum-pool-size: 10
      minimum-idle: 5
      connection-timeout: 20000
  jpa:
    hibernate:
      ddl-auto: validate
    show-sql: false
    open-in-view: false
    properties:
      hibernate:
        format_sql: true
        default_batch_fetch_size: 16

# For deep nesting (JPA, Hibernate, Actuator): YAML wins clearly
# For shallow config (single-level properties): .properties is simpler

# ── CHOOSE PROPERTIES WHEN: ───────────────────────────────────────────
# - Team is more comfortable with properties format
# - Configuration is mostly shallow (few nested levels)
# - You frequently append single values
# - You want to avoid YAML indentation errors
# - You need @PropertySource or @TestPropertySource files

# ── CHOOSE YAML WHEN: ─────────────────────────────────────────────────
# - Configuration has deep nesting (datasource, JPA, security)
# - You use multi-document profiles (--- separators)
# - You need lists or maps in configuration
# - Team prefers the structured format

# ── MIXING IS POSSIBLE BUT NOT RECOMMENDED: ───────────────────────────
# Spring Boot reads both application.properties AND application.yml.
# If both define the same key in the same location, .properties wins.
# Stick to one format per project for consistency.

Verify What Spring Boot Actually Loaded

Because Spring Boot ignores unknown keys, the only reliable way to know that your YAML did what you intended is to look at the effective configuration. 1. Actuator env endpoint: expose env (and configprops) and call /actuator/env/spring.datasource.url. The response lists every property source that defines the key and which one won, including the file and the profile. Values of keys that look like secrets are masked. 2. Startup log: run with --debug, or set logging.level.org.springframework.boot.context.config=TRACE to see which application*.yml files and documents were loaded and which profiles were active. 3. Active profile: the log line "The following 1 profile is active" is printed at startup; check it matches your expectation. 4. Typed binding: use @ConfigurationProperties with @Validated so that missing or mistyped required values fail at startup instead of silently using defaults. 5. IDE support: add spring-boot-configuration-processor and use an IDE with Spring support, which highlights unknown keys and suggests valid ones. For secrets, do not commit real values to application.yml. Use environment variables, placeholders such as ${DB_PASSWORD}, config trees (spring.config.import=optional:configtree:/run/secrets/) or a secrets manager.
yaml
# application.yml — expose the endpoints temporarily (do NOT expose env publicly in production)
management:
  endpoints:
    web:
      exposure:
        include: health,info,env,configprops

# Then:
#   curl http://localhost:8080/actuator/env/spring.datasource.url
#   {
#     "property": { "source": "applicationConfig: [classpath:/application-dev.yml]", "value": "jdbc:h2:mem:devdb" },
#     "propertySources": [ ... every source that defines the key ... ]
#   }
#
#   curl http://localhost:8080/actuator/configprops      # all @ConfigurationProperties beans and their bound values

# See which config files were loaded:
#   java -jar app.jar --debug
#   java -jar app.jar --logging.level.org.springframework.boot.context.config=TRACE

Frequently Asked Questions

What is application.yml in Spring Boot? It is Spring Boot's configuration file in YAML format. It holds the same settings as application.properties, written as an indented hierarchy. Should I use application.yml or application.properties? Either works. YAML is more readable for nested configuration, lists and multiple profiles; properties are simpler and have no indentation rules. Use one format per project. Where do I put application.yml? In src/main/resources, so it ends up at the classpath root. Spring Boot also reads a file next to the jar, and in a config/ folder, which override the packaged one. How do I use environment variables in application.yml? Reference them as placeholders: password: ${DB_PASSWORD}, with an optional default: ${DB_USERNAME:app}. You can also override any property from outside with a matching environment variable, such as SPRING_DATASOURCE_URL, without any placeholder. How do I define profiles in application.yml? Create application-dev.yml and application-prod.yml, or use one file with --- separators and spring.config.activate.on-profile in each document. Activate a profile with spring.profiles.active, SPRING_PROFILES_ACTIVE or --spring.profiles.active. What replaced spring.profiles in YAML documents? spring.config.activate.on-profile (since Spring Boot 2.4). Can application.yml and application.properties be used together? Yes. Both are loaded, and .properties takes precedence for keys defined in both. Avoid mixing them. How do I write a list in application.yml? Use dashes on separate lines, or inline brackets: allowed-origins: [a, b]. Bind it to a List<String> field. Lists from higher-priority sources replace lower-priority lists completely. Why is my YAML property ignored? Usually a wrong indentation, a typo in the key, a file in the wrong location, or an inactive profile. Check /actuator/env to see what was actually loaded. Why does yes or NO turn into true or false? SnakeYAML follows YAML 1.1, where yes, no, on and off are booleans. Quote the value ("NO"). Why am I getting "mapping values are not allowed here"? An unquoted value contains a colon followed by a space. Put the value in double quotes. Can I load a YAML file with @PropertySource? No, @PropertySource does not parse YAML. Use spring.config.import (for example optional:classpath:extra.yml) or @ConfigurationProperties. Is the file called application.yml or application.yaml? Both extensions are supported.

Key Takeaways

- application.yml and application.properties set the same properties; YAML nests them, and .properties wins if both define a key. - Indent with spaces, never tabs. A wrong indentation silently changes the key, and unknown keys are ignored, so verify with /actuator/env. - Quote values that YAML could misread: yes/no/on/off, NO, 1.10, 0755, values with ": ", " #", or starting with %, *, @. - Duplicate keys stop the application at startup; lists are replaced across sources, maps are merged. - Use --- documents with spring.config.activate.on-profile (Boot 2.4+) or application-{profile}.yml. Do not set active profiles inside profile-specific documents. - Environment variables, command-line arguments and system properties override the file; use ${NAME:default} and SPRING_* variables for deployment settings and secrets. - Bind structured YAML to records with @ConfigurationProperties and @Validated. - Related topics: application.properties, Profiles, @ConfigurationProperties, Externalized Configuration, Actuator Endpoints.