How does a `configtree:` import map a directory of files to Spring properties?
answer
- one file = one property
- path = key, contents = value
- trailing slash required (directory)
- trailing newline trimmed for String
- binds String or byte[]; * wildcard one level
basics
~20 sconfigtree: 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.
solid answer
~40 s`spring.config.import=configtree:/etc/config/` treats a directory as a property source (`ConfigTreePropertySource`). Every file under the root becomes a property: the **relative path is the key** and the **file contents is the value**. Nested directories map to dot/slash-separated keys, so a file `/etc/config/spring/datasource/password` becomes property `spring.datasource.password` (path separators normalize to dots). Filenames may also contain dots directly. Values are read as the raw file bytes and can bind to `String` or `byte[]`; Boot trims a single trailing newline for `String` bindings. The location **must end with `/`** to signal it's a directory. This is the canonical way to consume Kubernetes/Docker secret and configmap volumes, where each key is projected as an individual file — no parsing of a combined file, and secrets never sit in committed config.
code
java · 15 lines// On disk (mounted by k8s Secret/ConfigMap):
// /etc/config/spring.datasource.username -> contents: "appuser"
// /etc/config/spring.datasource.password -> contents: "s3cr3t\n"
//
// application.properties:
// spring.config.import=optional:configtree:/etc/config/
//
// Result properties (trailing \n trimmed for the String bind):
// spring.datasource.username = appuser
// spring.datasource.password = s3cr3t
import org.springframework.boot.context.properties.ConfigurationProperties;
@ConfigurationProperties("spring.datasource")
public record DataSourceProps(String username, String password) { }go deeper
Know that each file becomes a property with filename→key, contents→value.
Explain path-to-key normalization, the required trailing slash, newline trimming, and String/byte[] binding.
Cover the wildcard for multiple mounts, Origin tracking, and how it maps onto k8s Secret/ConfigMap volume projection.
Reason about config-tree as the file-based contract between orchestrator and app, and key-naming conventions that align with @ConfigurationProperties prefixes.
## The mapping rule A **config tree** is a directory in which **one file = one property**. When you write `spring.config.import=configtree:/etc/config/`, Boot creates a `ConfigTreePropertySource` rooted at `/etc/config/`. For each regular file it finds: - **key** = the file's path *relative to the tree root*, with OS path separators normalized to `.` — so `/etc/config/spring.datasource.password` → key `spring.datasource.password`, and a nested file `/etc/config/spring/datasource/password` → the same `spring.datasource.password`. - **value** = the **contents** of that file. The trailing `/` on the location is **required** — it tells Boot the target is a directory (a config tree), not a single file. ## Why this shape Container orchestrators project configuration as **files, not env vars or a merged blob**. A Kubernetes `Secret` or `ConfigMap` mounted as a volume yields one file per key (e.g. `/etc/config/db-password` containing the secret string). `configtree:` reads exactly that layout, so the app consumes secrets **without any committed values** and without custom parsing. ## Value handling and binding - Values are the raw file bytes. Binding to a `String` property gives the text; Boot strips a single trailing newline (files created by editors/orchestrators often end in `\n`). - You can bind a config-tree value to `byte[]` (or an `InputStreamSource`-style target) when the secret is binary — e.g. a keystore. - Origin tracking: each value carries an `Origin` pointing at the backing file, which shows up in `/actuator/env` and error messages. ## Wildcards for multiple mounts Boot supports a `*` wildcard *one level deep* so you can import several mounted volumes at once: ```properties spring.config.import=optional:configtree:/etc/config/*/ ``` This imports each immediate subdirectory of `/etc/config/` as its own tree — handy when multiple Secrets/ConfigMaps are each mounted under their own folder. ## Gotchas - **Forgetting the trailing `/`** makes Boot treat the location as a file and fail to resolve it as a tree. - **Deeply nested keys**: `/etc/config/a/b/c` → `a.b.c`; make sure your key layout matches your `@ConfigurationProperties` prefixes. - Kubernetes projects the ConfigMap/Secret key name verbatim as the filename, so choose keys that are valid property names (dots are fine as filenames). - Config-tree values participate in **relaxed binding** like any other property source. - Combine with `optional:` so local dev (no mount) doesn't crash.
- Why must the configtree: location end with a slash?The trailing `/` signals to Boot that the location is a directory to be treated as a config tree, rather than a single file to load as properties/YAML.
- How would you import several separately-mounted secret directories at once?Use the one-level wildcard: `spring.config.import=optional:configtree:/etc/config/*/`, which imports each immediate subdirectory as its own tree.
saying these in an interview costs you the question
- Saying the file is parsed as key=value lines (no — the whole file content is a single value)
- Thinking the filename is the value and contents the key (it's the reverse)
- Omitting the required trailing slash
- Assuming values keep their trailing newline (Boot trims a single trailing newline for String binding)