Skip to content

Hot Config Reload

Triggering a reload

Two mechanisms trigger a hot reload:

Terminal window
# Unix signal — sends SIGHUP to the running server
kill -HUP $(pgrep rws)
# or by PID
kill -HUP $(pidof rws)
Terminal window
# HTTP endpoint — no request body required
curl -X POST http://localhost:8080/admin/config/reload

Both re-parse rws.config.toml, update process environment variables, apply the new rate-limit thresholds to the live RateLimiter, and publish a new ConfigSnapshot atomically. On http2/http3 builds, SIGHUP also rebuilds the TlsAcceptor with fresh certificates for every virtual host.

What reloads without restart

SettingEnv var
CORS — all fieldsRWS_CONFIG_CORS_*
Rate-limit max requestsRWS_CONFIG_RATE_LIMIT_MAX_REQUESTS
Rate-limit window (seconds)RWS_CONFIG_RATE_LIMIT_WINDOW_SECS
Log format (combined / json)RWS_CONFIG_LOG_FORMAT
Request allocation sizeRWS_CONFIG_REQUEST_ALLOCATION_SIZE_IN_BYTES
TLS certificates (http2+ builds)RWS_CONFIG_TLS_CERT_FILE, RWS_CONFIG_TLS_KEY_FILE

What requires a restart

SettingWhy
IP addressBound socket cannot be moved
PortBound socket cannot be moved
Thread countThread pool is fixed at startup

ConfigSnapshot

Call config_reload::current() anywhere in the handler stack to get a typed snapshot of all hot-reloadable values at that instant. The call takes a brief read lock and clones a handful of strings — safe to call on every request.

use rust_web_server::config_reload;
fn my_handler(req: &Request, conn: &ConnectionInfo) -> Response {
let cfg = config_reload::current();
if cfg.cors_allow_all {
// serve with open CORS
}
println!(
"rate limit: {}/{} log: {}",
cfg.rate_limit_max_requests,
cfg.rate_limit_window_secs,
cfg.log_format,
);
Response::new()
}

ConfigSnapshot fields

FieldTypeSource env var
cors_allow_allboolRWS_CONFIG_CORS_ALLOW_ALL
cors_allow_originsStringRWS_CONFIG_CORS_ALLOW_ORIGINS
cors_allow_methodsStringRWS_CONFIG_CORS_ALLOW_METHODS
cors_allow_headersStringRWS_CONFIG_CORS_ALLOW_HEADERS
cors_allow_credentialsStringRWS_CONFIG_CORS_ALLOW_CREDENTIALS
cors_expose_headersStringRWS_CONFIG_CORS_EXPOSE_HEADERS
cors_max_ageStringRWS_CONFIG_CORS_MAX_AGE
rate_limit_max_requestsu32RWS_CONFIG_RATE_LIMIT_MAX_REQUESTS
rate_limit_window_secsu64RWS_CONFIG_RATE_LIMIT_WINDOW_SECS
log_formatStringRWS_CONFIG_LOG_FORMAT
request_allocation_sizei64RWS_CONFIG_REQUEST_ALLOCATION_SIZE_IN_BYTES

Triggering reload from code

RELOAD_REQUESTED is an AtomicBool in the config_reload module. Set it to true to trigger a reload on the next connection cycle, or call config_reload::reload() directly from any thread:

use rust_web_server::config_reload;
// Option A: set the flag (picked up between connections)
config_reload::RELOAD_REQUESTED.store(true, std::sync::atomic::Ordering::SeqCst);
// Option B: call reload() immediately from a handler
fn reload_handler(_req: &Request, _conn: &ConnectionInfo) -> Response {
config_reload::reload();
Response::new() // 200 OK
}

Pattern: dynamic behaviour from live config

fn rate_limit_info(_req: &Request, _conn: &ConnectionInfo) -> Response {
let cfg = config_reload::current();
let body = format!(
r#"{{"max_requests":{},"window_secs":{}}}"#,
cfg.rate_limit_max_requests, cfg.rate_limit_window_secs
);
Response::json(&body)
}