
The Claude Code Changelog· Folge 172
Preventing Premature Runner Shutdowns in Claude Code 2.1.311
Donnerstag, 29. Oktober 2026 · 5 min
Kapitelklicken springt direkt hin
Shownotes
Learn how Claude Code 2.1.311's new self-hosted runner idle-hold flags prevent background processes and dev servers from being terminated prematurely. We explore how to configure these settings, avoid common timing misconfigurations, and handle teardown caveats.
Show Notes
• Self-hosted environments reference - Claude Code Docs: code.claude.com/docs/en/self-hosted-environmen…
Transkriptvom Podcast bereitgestellt · Zeitmarken springen hin
Right, so picture this. Your agent finishes a turn, the dev server it started is humming along in the background, and the runner decides the session is idle and packs it up. Server gone. That's what today's about. This is The Claude Code Changelog, brought to you by Jellypod AI, and I'm Lachlan. And I'm James.
Claude Code 2.1.311 added two flags to the self hosted runner that fix exactly that. First one is the hold idle file flag, with the environment variable SELF_HOSTED_RUNNER_HOLD_IDLE_FILE. Second is the max hold min flag, with SELF_HOSTED_RUNNER_MAX_HOLD_MS.
Quick scope note self hosted environments are in public beta on Team and Enterprise
plans, so this is for platform and DevOps folks running their own runners. And the problem is pretty specific, yeah? Before this, when a session finished a turn with no interactive command in flight, the runner treated it as idle. Then it released or drained it, based on the release idle session min setting or your autoscaler's shutdown policy.
But devs spawn background stuff all the time. Local dev servers, a mock API, Docker test containers. Runner can't tell those matter. Right, and that's the gap the new flag fills. You point it at a file path. The docs describe it as a file your host keeps changing to hold an idle session on the runner.
So the runner isn't guessing about your processes. Your host is the one that knows whether the mock API is still up, and it signals that by touching or updating that file. While the file keeps changing, the idle session stays put. When the file goes quiet, normal idle release takes over. So the runner's basically asking, mate, are we still busy?
And your script answers by poking a file. Pretty much. Here's a sketch of how you'd wire it. This is my illustration, not something from the docs. You run a small loop on the host that checks whether your background service is alive, and if it is, it touches the file every so often. Then you start the runner with the hold idle file flag pointing at that path, or set SELF_HOSTED_RUNNER_HOLD_IDLE_FILE in your service definition.
If both the flag and the environment variable are set, the flag wins, which is true for most runner options. Now the bit that'll save you a nasty surprise. The max hold min flag. Default is 1440 minutes, so twenty four hours. It releases a held session anyway, that many minutes after it went idle. Because otherwise some forgotten process holds a runner host hostage forever, and you're paying for a box doing nothing.
And two edge cases worth memorizing. Setting it to zero turns the hold off entirely. And if the value is at or below the release idle session min setting, nothing gets held at all. So if you set max hold to ten minutes and your idle release is fifteen, you've configured a hold that never happens. Check those two numbers against each other.
Also, the env var is in milliseconds, with that MS suffix, while the flag is in minutes. Easy to get wrong in a Helm value. Caveats, then. First, container isolation. In a setup where each session gets its own ephemeral container, that teardown now waits until the hold releases. So a held session can sit there for up to your max hold window.
Second, stop timeouts. If your host sends a stop signal and your timeout is shorter than the hold windows you've allowed, the host might kill things before the runner finishes its shutdown. So raise those together. And this connects nicely to the Monitor episode, 2.1.310, where we talked about replacing polling.
Monitor is about the session watching a background process. This is the runner infrastructure side, making sure the process is still there when the session comes back for it. Also, it needs 2.1.311 or later on the runner, so check your fleet versions first. A few smaller bits from 2.1.311 and 2.1.312.
The Claude apps gateway gets an oidc.user_id_claim setting, so if you're on Entra ID you can identify users by the oid claim instead of sub. Full screen mode now has click to send for queued prompts. And the setup scripts for cloud environments now get a bash editor with syntax highlighting. That's the episode.
If you run self hosted runners, today's job is simple: pick your max hold value, make sure it's above your idle release threshold, and raise your stop timeout to match.