Layers & resolution
When you read from a ConfigStack, dotcfg combines its sources and works out the value your application should see. This page explains those rules before you have to depend on them.
Later layers win
The first layer has the lowest priority. Each layer after it may replace values from the layers before it. This ordering is called precedence: it answers “which value wins when more than one source provides the same setting?”
Nested settings are combined
Replacing one nested setting should not make unrelated settings disappear. When both sides are maps or tables, ConfigStack combines their keys and only replaces the parts that overlap. This is often called a deep merge.
# defaults
[server]
host = "127.0.0.1"
port = 3000
tags = ["stable"]
# project
[server]
port = 8080
tags = ["local"]
# result
server.host = "127.0.0.1"
server.port = 8080
server.tags = ["local"]Notice that server.host survives because the project layer did not replace it. The port changes because both layers define it.
Lists behave differently. ConfigStack does not try to guess how two lists should be combined, so a later list replaces the earlier list. Other single values, such as strings, numbers, and booleans, replace earlier values too.
Sources can use different formats
One source can use TOML while another uses JSON or YAML. Each DotCfg reads its own file format first; ConfigStack then combines the resulting values. You do not need to convert all of your source files to the same format just to layer them.
What happens when a source has no values?
A source file may not exist yet. That is fine: ConfigStack skips the missing source and keeps resolving the others. A file that exists but is valid and empty contributes no settings.
A broken file is different. If a source contains malformed TOML, JSON, or YAML, ConfigStack returns the parse error instead of pretending that source was missing. This helps you notice a configuration mistake instead of silently running with older values.
Put environment variables on top
You can give the stack an environment prefix with with_env_prefix(). Matching environment variables then have higher priority than every file layer.
let cfg = ConfigStack::new()
.layer("defaults", defaults)
.layer("project", project)
.with_env_prefix("APP")
.build()?;ConfigStack uses two underscores to mark a nested key. For example, APP_DATABASE__PORT overrides database.port. A normal underscore is kept as part of the key, so APP_API_KEY means api_key, not api.key.
The final order is therefore: earlier file layers, later file layers, then environment variables.
Next, learn how to read the result and choose which source receives writes.