How to hot-reload configuration
package watch turns the configuration cascade into a live stream for long-running processes. It uses fsnotify and re-runs the full resolve + validate pipeline when watched files change. The full function catalogue is in package watch.
Watch the app’s configuration
Section titled “Watch the app’s configuration”func (d *Daemon) Run(ctx context.Context, session *conga.Session) error { onChange := func(newCfg *CLI, err error) { if err != nil { fmt.Fprintf(session.Stderr(), "config invalid, keeping previous: %v\n", err)
return }
applyConfig(newCfg) // swap your settings atomically }
return watch.Config(ctx, session, onChange)}watch.Config[T](ctx, session, onChange):
- watches exactly the files reported by
session.LoadedConfigFiles()/session.CandidateConfigFiles()for the active execution; - calls
onChangewith a freshly bound, fully validated*Tafter each change (the same result assession.Reload[T](ctx)); - calls
onChangewith a non-nil error when a change produces an invalid configuration — your previous settings stay in effect; - returns
watch.ErrNoCandidateswhen the candidate set is empty.
Watch arbitrary files
Section titled “Watch arbitrary files”err := watch.File(ctx, "/etc/motd", func(err error) { ... })
err := watch.Paths(ctx, []string{"a.yaml", "b.yaml"}, func(err error) { ... })Paths watches any file list; File is the single-file convenience form.
Combining with signal shutdown
Section titled “Combining with signal shutdown”app.Run() cancels its context on SIGINT/SIGTERM. Block on the watch call inside your command; when the context is cancelled the watchers stop and your Finally hook performs teardown — see example 16 (16-live-config-watcher) for a complete daemon and About the lifecycle for teardown semantics.