Hot-Reload Configuration
tileserver-rs supports reloading configuration at runtime without dropping any in-flight requests. Two mechanisms are available: SIGHUP signal and an admin HTTP endpoint.
SIGHUP (Unix)
Send SIGHUP to reload configuration from the original config file:
kill -HUP $(pgrep tileserver-rs)
The server re-reads the config, rebuilds sources/styles/renderer, and atomically swaps the application state. Requests in progress complete against the old state; new requests use the new state.
Admin Endpoint
Enable the admin server by setting admin_bind in your config:
[server]
host = "0.0.0.0"
port = 8080
# admin_bind = "127.0.0.1:9099"
The default value 127.0.0.1:0 disables the admin server. Set a real port to enable it.
POST /__admin/reload
Trigger a reload via HTTP:
curl -X POST http://127.0.0.1:9099/__admin/reload
Response:
{
"ok": true,
"reloaded": true,
"config_hash": "a1b2c3...",
"loaded_at_unix": 1700000000,
"loaded_sources": 3,
"loaded_styles": 2,
"renderer_enabled": true,
"version": "2.7.1"
}
Force Reload
By default, reload is skipped if the config file hasn't changed (hash comparison). Use ?flush=true to force a full reload:
curl -X POST http://127.0.0.1:9099/__admin/reload?flush=true
No-Op Response
When the config hash matches the current state:
{
"ok": true,
"reloaded": false,
"config_hash": "a1b2c3...",
"loaded_at_unix": 1700000000,
"loaded_sources": 3,
"loaded_styles": 2,
"renderer_enabled": true,
"version": "2.7.1"
}
Runtime Metadata
The /ping endpoint on the main server returns current runtime state:
curl http://localhost:8080/ping
{
"status": "ok",
"config_hash": "a1b2c3...",
"loaded_at_unix": 1700000000,
"loaded_sources": 3,
"loaded_styles": 2,
"renderer_enabled": true,
"version": "2.7.1"
}
Use this for health checks, monitoring dashboards, or verifying that a reload took effect.
What Gets Reloaded
| Reloaded | Not Reloaded |
|---|---|
| Tile sources (PMTiles, MBTiles, PostgreSQL) | Server host/port |
| Map styles | Admin bind address |
| Native renderer | CORS configuration |
| Fonts/files directories | Telemetry settings |
| PostgreSQL connection pools | — |
| In-memory tile cache (dropped with old state) | — |
Host, port, and admin bind require a full restart since they involve bound TCP listeners.
PostgreSQL Sources
Reload fully rebuilds PostgreSQL connection pools and tile caches:
| Change | Reload needed? |
|---|---|
| Data changes (new rows, updated geometries) | No — queries run live per request |
Function changes (CREATE OR REPLACE FUNCTION) | No — functions are called per request |
| Config changes (connection string, add/remove source) | Yes |
| Schema changes (new table, altered columns) | Yes |
| Connection pool stale (server restart, failover) | Use ?flush=true to force new pool |
Between reloads, the in-memory tile cache (moka) serves cached tiles until TTL expires. On reload, the cache is dropped entirely since a new SourceManager is created.
How It Works
The server uses ArcSwap for wait-free atomic state replacement. On reload:
- Config file is re-read and hashed
- If hash matches current state, reload is skipped (unless
flush=true) - New sources, styles, and renderer are built from the updated config
- The new state is atomically swapped in (~1ns)
- Old state is dropped once all in-flight requests complete
If building the new state fails (invalid config, unreachable sources), the old state is preserved and an error is returned.