From a9f80d88023a6593e0f814ac32928e74317ac111 Mon Sep 17 00:00:00 2001 From: Igor Serganov Date: Tue, 28 Jul 2026 10:15:02 -0700 Subject: [PATCH] hscontrol: cancel tailsql on graceful shutdown Serve stored a Background context for tailsql and called context.Done() during signal shutdown. Done only returns the done channel and does not cancel, so tailsql never stopped when Headscale shut down. Create a cancellable child of the Serve context, run `runTailSQLService` in the listener errgroup so its error is surfaced, and call the cancel func on shutdown - tailsql unblocks on ctx.Done. --- hscontrol/app.go | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/hscontrol/app.go b/hscontrol/app.go index 22c0a2668..630590b8c 100644 --- a/hscontrol/app.go +++ b/hscontrol/app.go @@ -736,7 +736,7 @@ func (h *Headscale) Serve() error { log.Info().Msg("metrics server disabled (metrics_listen_addr is empty)") } - var tailsqlContext context.Context + var tailsqlCancel context.CancelFunc if tailsqlEnabled { if h.cfg.Database.Type != types.DatabaseSqlite { @@ -751,9 +751,12 @@ func (h *Headscale) Serve() error { log.Fatal().Msg("tailsql requires TS_AUTHKEY to be set") } - tailsqlContext = context.Background() + var tailsqlCtx context.Context + tailsqlCtx, tailsqlCancel = context.WithCancel(ctx) - go runTailSQLService(ctx, util.TSLogfWrapper(), tailsqlStateDir, h.cfg.Database.Sqlite.Path) //nolint:errcheck + errorGroup.Go(func() error { + return runTailSQLService(tailsqlCtx, util.TSLogfWrapper(), tailsqlStateDir, h.cfg.Database.Sqlite.Path) + }) } // Handle common process-killing signals so we can gracefully shut down: @@ -832,9 +835,9 @@ func (h *Headscale) Serve() error { log.Error().Err(err).Msg("failed to shutdown socket server") } - if tailsqlContext != nil { + if tailsqlCancel != nil { info("shutting down tailsql") - tailsqlContext.Done() + tailsqlCancel() } // Close network listeners