skip to content

Config import, trees & secrets

spring.config.import pulls in extra config, including config trees where a mounted Kubernetes or Docker secret file becomes a property. This is the modern answer to 'how do you get secrets into the app without committing them'.

part ofSpring Frameworkoverview, primer and where to startread it →
on this pageshow

questions

5

What does the `spring.config.import` property do, and what is the effect of the `optional:` prefix?

level: juniorimportance: must knowfreq 55%

answer

  1. import extra config sources at startup
  2. no optional = fail if missing (ConfigDataResourceNotFoundException)
  3. optional: = skip silently
  4. importer wins over imported (lower precedence)
  5. replaced additional-location

basics

~10 s

spring.config.import tells Spring Boot to pull in extra config from another location (a file, config tree, etc.) during startup. optional: means: if that location is missing, don't fail — just skip it.

solid answer

~40 s

`spring.config.import` is a Spring Boot 2.4+ property you put in `application.properties`/`application.yml` to import additional configuration sources during the config-data loading phase — extra property files, config trees (`configtree:`), or Config Server. By default, if the imported location does not exist, the application fails to start with a `ConfigDataResourceNotFoundException`. Prefixing the value with `optional:` (e.g. `optional:configtree:/etc/config/`) makes a missing resource a no-op instead of a fatal error. Imports are resolved in order, and the imported properties are placed with lower precedence than the document that declared the import, so the importing file's own values win on conflict. It replaced the older `spring.config.additional-location` mechanism for most use cases.

code

java · 14 lines
java
// application.properties
//
// Import a config tree that k8s mounts, but don't crash on a laptop
// where /etc/config does not exist:
//   spring.config.import=optional:configtree:/etc/config/
//
// Without optional:, a missing /etc/config/ throws at startup:
//   org.springframework.boot.context.config.ConfigDataResourceNotFoundException
//
// Binding class stays plain Spring:
import org.springframework.boot.context.properties.ConfigurationProperties;

@ConfigurationProperties("app.db")
public record DbProps(String url, String username, String password) { }

go deeper

for a junior

Know it imports extra config and that optional: prevents a crash when the source is missing.

for a middle

Explain the prefixes (configtree:, file:, configserver:) and the default fail-fast behavior.

for a senior

Discuss precedence (importer over imported), transitive imports, and container use with optional:configtree:.

for a principal

Frame it as the standardized config-composition mechanism replacing additional-location, and reason about fail-fast vs optional as an operational safety choice.

## What it is `spring.config.import` is a property, introduced in **Spring Boot 2.4**, that instructs Boot to load **additional configuration** while it is building the `Environment`. You declare it inside a config file that Boot already loads (`application.properties` / `application.yml`), and its value is one or more *locations*, comma-separated. ```properties spring.config.import=optional:file:./extra.properties,configtree:/etc/config/ ``` Each location has an optional **prefix** that selects how Boot resolves it: - no prefix / `file:` / `classpath:` — a normal properties or YAML file - `configtree:` — a *directory* where each file is one property (see the config-tree question) - `configserver:` — Spring Cloud Config Server ## The `optional:` prefix By default, **a location that cannot be found is an error**: startup aborts with `ConfigDataResourceNotFoundException`. This is deliberate — it protects you from silently running with missing config (e.g., a Kubernetes secret volume that failed to mount). Prefix the location with `optional:` to downgrade a missing resource to a silent skip: ```properties # fails hard if /etc/config/ is absent spring.config.import=configtree:/etc/config/ # tolerates absence — useful so the same config works on a laptop AND in k8s spring.config.import=optional:configtree:/etc/config/ ``` `optional:` can be combined with any other prefix and stacks in front of it (`optional:configtree:`, `optional:file:`). ## Ordering and precedence Imports are processed **in declared order**. Crucially, the properties from an imported document are inserted with **lower precedence than the document that performed the import** — so if both `application.properties` and an imported file define `foo`, the value in `application.properties` (the importer) wins. Imported documents may themselves declare further `spring.config.import` values (imports can be transitive). ## Why it exists / when to use It is the modern, uniform way to compose configuration from many sources — replacing the older `spring.config.additional-location`. It shines in containers: you keep committed defaults in `application.yml` and import environment- or secret-specific data (config trees mounted by Kubernetes/Docker) at runtime with `optional:configtree:`. ## Gotchas - Only works for **config-data** locations processed early; it is not a general-purpose `@PropertySource` replacement for arbitrary runtime loading. - Forgetting `optional:` on a location that only exists in production makes local runs crash. - Profile-specific imports: an import declared under a profile document is only processed when that profile is active.

  • What happens at startup if a non-optional imported location is missing?
    The application context fails to start and Boot throws `ConfigDataResourceNotFoundException`, naming the location it could not resolve.
  • If both application.properties and an imported file define the same key, which wins?
    The importing document (application.properties) wins — imported properties are placed at lower precedence than the file that declared the import.

saying these in an interview costs you the question

  • Claiming imported properties always override the importing file
  • Thinking spring.config.import is the same as @PropertySource (it runs in the earlier config-data phase, not bean loading)
  • Believing a missing location is silently ignored by default (it isn't — you need optional:)

context

open as a page

How does a `configtree:` import map a directory of files to Spring properties?

level: middleimportance: must knowfreq 50%

basics

~20 s

configtree: points at a directory. Each file becomes one property: the file's path (relative to the tree root) is the property name, and the file's contents is the value. It's built for secret files mounted as volumes.

open as a page

How does Spring Boot bind sensitive values supplied via environment variables, and where do env vars and imported config trees sit in the property-source precedence order?

level: seniorimportance: should knowfreq 45%

basics

~20 s

An env var like SPRING_DATASOURCE_PASSWORD maps via relaxed binding to spring.datasource.password. OS environment variables sit high in precedence — above application.properties/application.yml — so they override committed defaults, which is exactly what you want for secrets.

open as a page

How do you wire Kubernetes Secrets into a Spring Boot app using config trees, and why is that preferable to putting secrets in committed config or a ConfigMap?

level: seniorimportance: should knowfreq 40%

basics

~20 s

Mount the Secret as a volume so each key becomes a file, then spring.config.import=optional:configtree:/etc/secrets/. Spring reads the files as properties. Secrets stay out of the image, out of git, and out of ConfigMaps (which aren't encrypted).

open as a page

Design a secrets-management strategy for a Spring Boot service across local, CI, and Kubernetes prod. What are the tradeoffs of env vars vs config trees vs an external secrets manager, and how do you keep secrets from leaking through Spring's own surfaces (actuator, logs)?

level: principalimportance: should knowfreq 28%

basics

~20 s

Commit only non-secret defaults. Locally use an untracked file or env vars; in CI use masked pipeline secrets; in prod mount Kubernetes Secrets as a config tree (optional:configtree:) or pull from Vault. Lock down /actuator/env, rely on Boot's sanitizer, and never log secret values.

open as a page