🐛 tunnel diag: Docker log collection deadlocks when the container's stderr log exceeds 64 KiB

Open Beginner friendly
#1,783 1 comment 0 reactions 0 assignees View on GitHub

Maintainers usually reply within 1 day

@akasakariko is already working on this.

Since Oct 30, 2026.

  • #1784 by @akasakariko — open

Assessment

Difficulty
2/5
Estimated time
1-3 hours
Newbie friendliness
76/100
Issue type
Bug
Clarity
Clearly specified
Activity status
Active
Tech stack
docker, go, kubernetes
Domain
devops, tooling

Research direction

Start with diagnostic/log_collector_utils.go and inspect PipeCommandOutputToFile, then check how the host and Kubernetes collectors in log_collector_host.go and log_collector_kubernetes.go use it. Reproduce with Docker logs exceeding 65,536 bytes on stderr and verify both streams are collected without a timeout; confirm the shared diagnostic jobs complete afterward.

Written by the indexing model from the issue text.

Description

Version: cloudflared 2026.9.3 (the same code is on master today), cloudflared tunnel diag --metrics localhost:<port> --diag-container-id <container>, Linux.

What happens: "log information" fails with error waiting from command 'docker logs --tail 10000 --since <t> <container>': signal: killed after 45 s. Because every job shares the one 45 s context in RunDiagnostic, the jobs after it (raw network information, network information, cli configuration, configuration) also fail with context deadline exceeded.

Cause: diagnostic/log_collector_utils.go, PipeCommandOutputToFile, opens both StdoutPipe and StderrPipe, then io.Copys stdout to EOF before it starts reading stderr. cloudflared writes its log to stderr, so docker logs replays it on stderr. Once more than the pipe buffer (64 KiB on Linux) is pending on stderr, docker logs blocks writing stderr, never closes stdout, and the stdout copy waits until the context kills the process.

Reproduce: run diag against any Docker connector whose docker logs <container> 2>&1 >/dev/null | wc -c is above 65,536. Ours: 160,835 bytes on stderr, 0 on stdout → deadlock every time; another connector with 9,222 bytes → succeeds.

Workaround we used: a docker wrapper earlier in PATH: exec /path/to/real/docker "$@" 2>&1.

Suggested fix: read both pipes concurrently, or set command.Stdout and command.Stderr to the same *os.File (exec then copies both without a reader race), e.g. command.Stdout = outputHandle; command.Stderr = outputHandle; err := command.Run(). The same function serves the host and Kubernetes collectors (log_collector_host.go, log_collector_kubernetes.go), so kubectl logs would deadlock the same way.

Dominant language
Go
Stars
16.3k
Forks
1.5k
PR merge metrics
No merged PRs in 30d

Getting set up

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

More from cloudflare/cloudflared

All issues in cloudflare/cloudflared

Similar issues

More Go issues

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.