Hot-reload policy without dropping connections

Problem: you want to change a deny/redact/rate-limit rule on a running gateway without dropping any in-flight requests.

Solution: edit mcpgw.yaml and send SIGHUP to the mcpgw process. It re-reads the config, validates it, and atomically swaps the policy engine.

Recipe

# Edit the file in-place
$EDITOR /etc/mcpgw/mcpgw.yaml

# Reload (Docker)
docker kill --signal=HUP mcpgw

# Reload (systemd)
systemctl kill --signal=HUP mcpgw.service

# Reload (raw process)
kill -HUP "$(pidof mcpgw)"

The gateway logs:

policy reload: ok (rules=4)

Or, on a config error:

policy reload: rejected: invalid regex on rule "redact-secrets": missing closing bracket

If the new config is invalid, the old policy stays in effect. The gateway never enters a half-loaded state.

What is reloadable, what is not

FieldHot-reload?
policy.rules✅ yes
audit.path, audit.max_size_mb, audit.compress_rotated✅ yes
telemetry.customer.endpoint, telemetry.customer.service_name⚠️ requires restart
upstreams[], routes[], default_upstream⚠️ requires restart
listen, license.path⚠️ requires restart

For non-reloadable fields, do a rolling restart: bring up a second container with the new config, drain the first.

Pitfalls

  • SIGHUP only reloads mcpgw.yaml, not the license. License is re-validated hourly on its own schedule; to force a license re-read, restart.
  • In-flight requests keep using the old engine. A request that arrives during reload but was admitted before the swap will be evaluated against the previous rule set. New requests get the new engine. This is a feature, not a bug — it prevents partial application.
  • Audit log records the reload with event: "policy_reload" and the new rule count. Failed reloads are logged with event: "policy_reload_failed" and the validation error.

Verifying

# Trigger a reload and watch the log
tail -f audit.jsonl | jq -c 'select(.event | startswith("policy_reload"))' &
docker kill --signal=HUP mcpgw

You should see one new line per reload.