Hacktoberfest 2026: le issue che i maintainer hanno segnato per ottobre, aperte e adatte ai principianti. Sfoglia le issue Hacktoberfest

🐛 After SIGTERM, `cloudflared tunnel run` uses 100% of one CPU core for the whole graceful-shutdown period

Aperta Adatta ai principianti
#1,753 3 commenti 0 reazioni 0 assegnatari Vedi su GitHub

@cyphercodes ci sta già lavorando.

Dal 8/10/2026.

  • #1755 di @cyphercodes — aperta
  • #1760 di @theolujay — aperta

Valutazione

Difficoltà
1/5
Tempo stimato
Meno di un'ora
Idoneità per principianti
88/100
Tipo di issue
Bug
Chiarezza
Specificata chiaramente
Stato di attività
Attiva
Stack tecnologico
go
Ambito
backend

Direzione di ricerca

L’issue indica il busy loop in supervisor/supervisor.go, all’interno del select di Supervisor.Run su gracefulShutdownC. Leggi quel loop e la gestione dei segnali in waitForSignal; initialize() non è interessato. Verifica che dopo SIGTERM il supervisor si blocchi durante il drain e che continui a terminare quando le connessioni del tunnel finiscono o scade il periodo di grazia.

Scritto dal modello di indicizzazione a partire dal testo della issue.

Descrizione

Priority: Normal Type: Bug

Describe the bug
After SIGTERM, cloudflared tunnel run uses 100% of one CPU core for the whole graceful-shutdown period (up to --grace-period). It uses ~0% before the signal, including while it serves a long-lived request.

The cause is a busy loop in Supervisor.Run (supervisor/supervisor.go). On SIGTERM, waitForSignal calls close(graceShutdownC). The supervisor's main loop selects on that channel:

case <-s.gracefulShutdownC:
    shuttingDown = true

A closed channel is always ready, so this case is selected on every iteration and the for { select { ... } } loop never blocks. The loop runs until the last tunnel connection exits or the process is killed.

To Reproduce

  1. Run a local origin that serves a long-lived response (e.g. an SSE endpoint that writes one event per second for 90s).
  2. Run cloudflared tunnel --grace-period 100s --no-autoupdate --metrics 127.0.0.1:20241 run --token <token>
  3. Open the long-lived request through the tunnel hostname.
  4. kill -TERM <cloudflared pid>
  5. Sample CPU (e.g. /proc/<pid>/stat utime+stime over 3s) or watch top: cloudflared stays at 100% of one core until it exits.
  6. curl '127.0.0.1:20241/debug/pprof/goroutine?debug=2' shows the supervisor goroutine runnable inside the Run select loop, while the rest are blocked.

If it's an issue with Cloudflare Tunnel:
4. Tunnel ID: f499dceb-781e-470b-9943-3c1aa7674c30
5. cloudflared config: no config file; remotely managed tunnel run with --token, --grace-period 100s, --no-autoupdate. Protocol: QUIC (default). The loop is in the supervisor, so it should not depend on the protocol.

Expected behavior
While draining, cloudflared should sit near-idle, blocked until in-flight requests finish or the grace period ends.

Environment and versions

  • OS: Linux (Debian 13 trixie container, kernel 7.1.5)
  • Architecture: AMD64 (x86_64)
  • Version: 2026.9.3 (built 2026-09-24). The same code is on master.

Logs and errors
CPU measured over 3s windows:

idle       cpu%: 0
streaming  cpu%: 0
drain1     cpu%: 100
drain2     cpu%: 100
drain3     cpu%: 101

Log at SIGTERM:

INF Initiating graceful shutdown due to signal terminated ...
INF Unregistered tunnel connection connIndex=2 event=0 ip=198.41.192.7
INF Unregistered tunnel connection connIndex=1 event=0 ip=198.41.192.57

Goroutine dump during drain (54 goroutines; only the pprof handler is running and this one is runnable):

goroutine 61 [runnable]:
context.(*cancelCtx).Done(...)
	/usr/local/go/src/context/context.go:448
github.com/cloudflare/cloudflared/supervisor.(*Supervisor).Run(...)
	.../cloudflared/supervisor/supervisor.go:137 +0x324
github.com/cloudflare/cloudflared/supervisor.StartTunnelDaemon(...)
	.../cloudflared/supervisor/tunnel.go:104 +0x50
github.com/cloudflare/cloudflared/cmd/cloudflared/tunnel.StartServer.func5()
	.../cloudflared/cmd/cloudflared/tunnel/cmd.go:521 +0x74

Additional context
Suggested fix: set the channel to nil after the first receive. A nil channel is never ready in a select, so the loop blocks again.

case <-s.gracefulShutdownC:
    shuttingDown = true
    s.gracefulShutdownC = nil

initialize() also selects on gracefulShutdownC, but it returns immediately on receive, so it is not affected.

Impact: we run cloudflared as the ingress next to our app server and drain for up to 100s on each deploy. During that time cloudflared takes a full core away from the app server while the app server finishes in-flight requests.

Lingua principale
Go
Stelle
16k
Fork
1.5k
Metriche di merge delle PR
Nessuna PR unita negli ultimi 30g

Preparare l'ambiente

Come iniziare

  1. Leggi tutta la issue e poi la guida ai contributi del progetto.
  2. Commenta sulla issue per dire che te ne occupi tu — evita che due persone facciano lo stesso lavoro.
  3. Fai un fork del repository e lavora su un branch.
  4. Apri una pull request che faccia riferimento al numero della issue.

Altre issue di cloudflare/cloudflared

Tutte le issue di cloudflare/cloudflared

Issue simili

Altre issue su Go

Ricevi le nuove issue nella tua casella

Un breve riepilogo di issue GitHub adatte ai principianti.