Skip to content

Reload configuration at runtime

Three ways to pick up a config change without restarting, in increasing order of machinery: call reload() yourself, subscribe to changes, or let the hot-reload watcher call reload() for you.

Re-read the configuration on demand

cfg.reload()?;
let current = cfg.get();

reload() re-reads every registered source — embedded strings, files and environment variables — and swaps the result in atomically. Existing snapshots taken with get() keep the value they had; the next get() returns the new one.

If the parse fails, the stored value is left exactly as it was and the error comes back to you. A config file saved mid-edit cannot drop a running process onto defaults.

Good triggers for an explicit reload: a SIGHUP handler, an admin endpoint, a timer, or the start of each unit of work in a long-running batch.

React to a change rather than polling

let mut rx = cfg.subscribe();

tokio::spawn(async move {
    loop {
        if rx.changed().await.is_err() {
            break; // the Config was dropped
        }
        let current = rx.borrow().clone();
        apply_log_level(&current.log_level);
    }
});

rx.borrow() is valid immediately — the receiver holds the current value before any reload happens — and changed().await resolves after each successful reload. A failed reload wakes nobody.

Two properties worth designing around: subscribing late is safe (a new receiver sees the newest value, not the start-up one), and the channel keeps only the latest value, so two reloads in quick succession may wake a subscriber once. You are told that configuration changed, not which key changed.

Reload automatically when the file changes

Enable the feature:

rtb-config = { version = "0.6", features = ["hot-reload"] }

Then start the watcher and keep the handle alive:

let handle = cfg.watch_files()?;   // dropping this stops the watcher
// ... run the service ...
drop(handle);                      // stops watching

The watcher registers every path given to user_file, coalesces file-system events over a fixed 250 ms window, and calls reload() for each batch. Subscribers wake on each successful reload. A reload that fails is swallowed — a bad save does not kill the watcher.

watch_files returns ConfigError::Watch if no user_file was registered, or if a registered path does not exist yet. Create the file before starting the watcher; it cannot wait for one to appear.

Know what the watcher will miss

The watcher is reliable for writes into an existing file. It is not reliable for saves that replace the file — write-to-temp-then-rename, which is how atomic saves and several editors work. Reproduced on Linux: the first replacement may reload, and after it the watcher stops noticing anything at all, including ordinary in-place writes. It reports no error and the handle stays alive.

If your tool must not miss a change, do not rely on the watcher alone:

// belt and braces: watch, and also re-read on a timer
let _handle = cfg.watch_files()?;

tokio::spawn({
    let cfg = cfg.clone();
    async move {
        let mut tick = tokio::time::interval(std::time::Duration::from_secs(30));
        loop {
            tick.tick().await;
            let _ = cfg.reload();
        }
    }
});

Config is Clone and clones share the same stored value and subscriber channel, so the timer task and the rest of the process see the same configuration.

The full detail, including what this means for Kubernetes ConfigMap volumes, is in what rtb-config does not do.

Decide what is safe to reload

Reloading changes the value your code reads next. It does not re-run anything you did with the old value at start-up, so a setting only takes effect on reload if something re-reads it:

Setting Reloadable?
Log level, timeouts, feature flags, rate limits Yes — read per unit of work
Connection-pool size Only if something resizes the pool on change
Listen address, port No — the socket is already bound

For the middle row, subscribe and act. For the last, treat the change as requiring a restart and say so in your tool's own documentation.

Next