This issue respects the following points:
Description of the bug
When an HLS session fails to start (e.g. under CPU load: Stream not ready yet. Retry → Error starting stream after retrying → session shutdown), the ffmpeg process that was spawned for it is not killed. It keeps running to its full -t duration (which can be hours for a movie), writing segments into the channel's stream directory with no session tracking it.
This causes a death spiral on modest hardware:
- A tune fails under load → orphan ffmpeg left running, eating CPU.
- The client retries (or the user zaps back) → next attempt is even more starved → fails → another orphan.
- Repeat until the box is saturated. We observed 11 ffmpeg processes vs. 4 tracked sessions on a 4-core N100 (load average 26), including one orphan that had been running for ~5 hours with no corresponding entry in
/api/sessions at all. At that point every new tune fails (No master playlist found ...) while already-established streams keep playing — which looks to users like "random channels are broken."
Two related observations:
DELETE /api/channels/:id/sessions also leaks the ffmpeg. It returns 201 and removes the session record, but the ffmpeg process survives (verified: still running 10+ seconds later, runs to its -t duration). So the session-stop API cannot be used to clean up — it creates orphans.
- Natural idle reaping works correctly: when a client disconnects cleanly, both the session and its ffmpeg are gone within ~150s. The leak is specific to the failed-start and explicit-delete paths.
Diagnostic signature for anyone else hitting this: pgrep -cx ffmpeg much greater than the number of channels in GET /api/sessions = orphaned transcodes.
Reproduction steps
- Run Tunarr (Docker) on modest hardware (4-core Intel N100, VAAPI transcode) with channels that require transcoding.
- Tune several channels in quick succession (channel zapping from an HLS client), or concurrently tune 2-3 heavy channels, until a session start fails with
Stream not ready yet. Retry.
- Watch
ps -C ffmpeg vs GET /api/sessions.
- Alternatively: start a stream, then call
DELETE /api/channels/:id/sessions and watch the ffmpeg process survive.
What is the current bug behavior?
ffmpeg processes from failed session starts (and from DELETE /api/channels/:id/sessions) are never reaped. They accumulate, consume CPU for hours, and starve all subsequent session starts while the API stays healthy (200), so external health checks don't catch it.
What is the expected correct behavior?
When a session fails to start or is deleted via the API, the associated ffmpeg process should be killed (the same cleanup that already runs correctly on idle disconnect).
Tunarr version
Latest (1.3.8, Docker ghcr.io/chrisbenincasa/tunarr:latest)
FFMPEG encoder type
VAAPI
Deployment Type
Docker
What operating system are you using?
Debian 13 (LXC on Proxmox), Intel N100
Full server logs
2026-07-11T21:56:48.747Z [info]: channel-80-transcode still running after SIGTERM. Sending SIGKILL
2026-07-11T21:56:50.010Z [info]: channel-80-transcode exited. (signal=SIGKILL, code=-1, expected?=true)
2026-07-11T21:57:05.069Z [error]: Error starting stream after retrying {"sessionId":"e57e4dfd-bead-4d9c-9f75-af37e281e1d9","channel":"d6abfeea-...","sessionType":"hls"}
err: { "message": "Stream not ready yet. Retry",
at retries (/snapshot/dist/bundle.cjs:1390:4662)
at async mse.waitForStreamReady (/snapshot/dist/bundle.cjs:1390:3774) }
2026-07-11T21:57:05.085Z [error]: Received error from session. Shutting down {"sessionId":"80779740-..._hls"}
2026-07-11T21:57:05.115Z [error]: No master playlist found for channel 80779740-... at path /config/tunarr/streams/stream_80779740-.../playlist.m3u8. This could mean the stream is not ready.
After these log lines, three ffmpeg processes for channel 80779740-... were still alive (ps showed them running; the oldest orphan on the box had etimes ≈ 17,500s with no session). A docker restart tunarr was the only built-in way to recover.
This issue respects the following points:
Description of the bug
When an HLS session fails to start (e.g. under CPU load:
Stream not ready yet. Retry→Error starting stream after retrying→ session shutdown), the ffmpeg process that was spawned for it is not killed. It keeps running to its full-tduration (which can be hours for a movie), writing segments into the channel's stream directory with no session tracking it.This causes a death spiral on modest hardware:
/api/sessionsat all. At that point every new tune fails (No master playlist found ...) while already-established streams keep playing — which looks to users like "random channels are broken."Two related observations:
DELETE /api/channels/:id/sessionsalso leaks the ffmpeg. It returns 201 and removes the session record, but the ffmpeg process survives (verified: still running 10+ seconds later, runs to its-tduration). So the session-stop API cannot be used to clean up — it creates orphans.Diagnostic signature for anyone else hitting this:
pgrep -cx ffmpegmuch greater than the number of channels inGET /api/sessions= orphaned transcodes.Reproduction steps
Stream not ready yet. Retry.ps -C ffmpegvsGET /api/sessions.DELETE /api/channels/:id/sessionsand watch the ffmpeg process survive.What is the current bug behavior?
ffmpeg processes from failed session starts (and from
DELETE /api/channels/:id/sessions) are never reaped. They accumulate, consume CPU for hours, and starve all subsequent session starts while the API stays healthy (200), so external health checks don't catch it.What is the expected correct behavior?
When a session fails to start or is deleted via the API, the associated ffmpeg process should be killed (the same cleanup that already runs correctly on idle disconnect).
Tunarr version
Latest (1.3.8, Docker
ghcr.io/chrisbenincasa/tunarr:latest)FFMPEG encoder type
VAAPI
Deployment Type
Docker
What operating system are you using?
Debian 13 (LXC on Proxmox), Intel N100
Full server logs
After these log lines, three ffmpeg processes for channel
80779740-...were still alive (ps showed them running; the oldest orphan on the box had etimes ≈ 17,500s with no session). Adocker restart tunarrwas the only built-in way to recover.