Override settings with environment variables¶
Environment variables are the top layer: they beat both config files and embedded
defaults, and they are re-read on every build() and every reload().
Register the prefix¶
let cfg = Config::<MyConfig>::builder()
.embedded_default(include_str!("../defaults.yaml"))
.user_file("/etc/mytool/config.yaml")
.env_prefixed("MYTOOL_")
.build()?;
Include the trailing underscore. env_prefixed("MYTOOL") looks equivalent and is
not: the separator stays on the front of every key, nothing matches, and no error is
reported. This is the single most common reason an environment override appears to do
nothing.
Name the variable for the field you want to set¶
The prefix is stripped, the rest is lower-cased, and each remaining underscore becomes a level of nesting.
| Field in your struct | Variable to set |
|---|---|
port: u16 |
MYTOOL_PORT |
http.port (a nested struct) |
MYTOOL_HTTP_PORT |
http.tls.enabled |
MYTOOL_HTTP_TLS_ENABLED |
Matching on the variable name is case-insensitive, so mytool_port works as well as
MYTOOL_PORT.
Set a boolean, a number or a list¶
Values are parsed rather than taken as raw strings:
MYTOOL_PORT=8080 # number
MYTOOL_DEBUG=true # boolean
MYTOOL_HOSTS='[a.example,b.example]' # list of two strings
MYTOOL_HOST=127.0.0.1 # string, dots and all
A list replaces the whole list from the layer below; there is no append syntax.
A value that does not fit the field's type fails the build with ConfigError::Parse,
naming both the key and the prefix it came from:
configuration error: invalid type: found string "eighty", expected u16 for key "PORT" in `MYTOOL_` environment variable(s)
An empty variable is not the same as an unset one. MYTOOL_HOST= sets the field to
the empty string.
Work around a field name that contains an underscore¶
A flat field like max_retries cannot be set from the environment.
MYTOOL_MAX_RETRIES is read as the nested key max.retries, which your struct does
not have, so it is ignored — no error, no warning, and the value silently stays as
the file left it.
There is no flag to turn that off. Pick one of:
- Nest it.
retry: RetrySection { max: u32 }is reachable asMYTOOL_RETRY_MAX. - Rename it to a single word.
MYTOOL_RETRIESreaches a field calledretries. - Leave it file-only and document that
max_retriesis set in the config file rather than the environment.
Choosing between these is a design decision about which settings an operator must be able to override without editing a file — worth making deliberately when you name the field.
Apply a change without restarting¶
Environment variables are read at parse time, not cached at start-up, so a variable exported after the process started takes effect at the next reload:
// somewhere in the process, after MYTOOL_PORT changed
cfg.reload()?;
assert_eq!(cfg.get().port, 9999);
In practice a process cannot change its own environment from outside, so this matters for two cases: a supervisor that sets variables before an exec, and code inside the process that sets a variable and then reloads deliberately.
Accept more than one prefix¶
Register several. Later registrations win when the same key is set under both:
let cfg = Config::<MyConfig>::builder()
.env_prefixed("LEGACYTOOL_")
.env_prefixed("MYTOOL_")
.build()?;
That is the shape to use when renaming a tool without breaking the old variables.