If you installed OpenAI’s Codex CLI with npm install -g @openai/codex and you have version 0.157.0 or later, starting an interactive session may leave behind two things you did not ask for: a persistent background daemon that outlives every closed terminal window and runs from its own private copy of the package, and a detached updater process that downloads new releases and restarts the backend on its own schedule. The practical consequence is that the binary you installed and pinned is no longer necessarily the binary doing the work. You can verify all of this in about five minutes and shut it down with four commands, but the shutdown has a documented gap, and the release line is still changing the machinery. This article gives you the audit procedure, the two failure modes worth testing before you trust the tool in scripted contexts, and an honest accounting of what is and is not established.
What actually starts: the managed daemon, the private copy, and the updater
The core evidence is a single GitHub issue, openai/codex#48195, filed against CLI 0.157.0 installed via npm on Linux under WSL2. It is author-reported and unreplicated, and that qualification applies throughout. But it is unusually detailed, and the behaviors it describes are corroborated in kind, if not in every specific, by OpenAI’s own release notes.
Per the issue, when you open an interactive session, the CLI:
- Starts a persistent background daemon,
codex app-server --managed-daemon. In the reporter’s words: “It runs in its own session with no TTY and keeps running after every Codex window is closed (reparented to PID 1) until codex app-server daemon stop or a reboot.” - Copies the entire platform package, “~374 MB: codex, codex-code-mode-host, bundled bwrap/zsh/rg”, into
~/.codex/packages/app-server-daemon/releases/<version>/and runs the daemon from that copy, which the author describes as “byte-identical to what npm had just installed.” - Starts a detached updater,
codex app-server daemon pid-update-loop, that checks for updates “5 minutes after start and then hourly, downloads new releases and restarts the backend.” Note the cadence precisely: the first check happens five minutes after start, and subsequent checks are hourly. It is not a check every five minutes.
The reason this is more than a resource-footprint complaint is the decoupling. When you install a CLI through npm, Homebrew, or your distro’s package manager, you are choosing a version, and the package manager’s whole job is to keep what you installed in sync with what runs. A daemon that copies the package to a private location and updates itself on the vendor’s schedule inverts that: the version executing under ~/.codex/packages can drift from the version npm reports, and an update can land without any package-manager transaction for your fleet tooling to observe.
The issue cites a related report, #40969, for the claim that daemon auto-update “force-kills active turns after a 60s drain budget, and cannot be disabled.” That quoted phrase is the issue’s title, but the thread backs the mechanism with a source review verified against current main on 2026-08-27: STOP_GRACE_PERIOD = 60s, STOP_TIMEOUT = 70s, and bootstrap always starts the update loop; auto_update_enabled: true literal (unchanged). A disable-auto-update command pair is proposed in the same thread, patch prepared but not confirmed merged, so read “cannot be disabled” as holding as of that check rather than permanently.
The five-minute audit: detect the daemon and identify which binary is executing
Everything below follows the procedure the issue author reports worked on their machine. Given a single unreplicated report, treat these as a verification checklist to run yourself, not as guaranteed behavior on your platform.
1. Is the daemon running?
pgrep -af 'app-server'You are looking for codex app-server --managed-daemon running with no TTY and, on Linux, reparented to PID 1. A daemon you never explicitly started, still alive after you closed every Codex window, is the tell.
2. Is the private package copy present?
ls ~/.codex/packages/app-server-daemon/releasesPer the report, this directory holds the roughly 374 MB copy (including codex, codex-code-mode-host, and bundled bwrap/zsh/rg) that the daemon actually executes from. If the directory exists, the versioned path inside it tells you which copy is running, which may differ from npm ls -g @openai/codex.
3. Is the updater running?
pgrep -af 'pid-update-loop'This is the detached updater. Its presence matters because, as the next section covers, the documented stop command does not stop it.
If all three checks come back clean on your install, the report may simply not generalize to your version or platform. That is a legitimate result, and the rest of this article is then a watch item rather than an incident.
Stopping it completely: why codex app-server daemon stop is not enough
The natural first response is the documented stop command, and per the issue it is incomplete:
Bug found while turning it off: codex app-server daemon stop stops the app-server but leaves the pid-update-loop updater running (still alive 70 s later, had to kill it manually).
So a full shutdown is two steps:
codex app-server daemon stoppkill -f 'app-server daemon pid-update-loop' # not stopped by `daemon stop`Then re-run the pgrep checks to confirm both processes are gone. A stop command that leaves the component responsible for downloading and restarting the backend alive is exactly the kind of partial shutdown that fools a checklist, so verify rather than assume.
Opting out: daemon_auto_start and what it covers
Here the counter-evidence matters, because the honest picture is better than the alarmist one. The issue title asks OpenAI to make daemon_auto_start opt-in, but the author’s own report shows auto-start can be disabled today:
codex features disable daemon_auto_startcodex app-server daemon stoppkill -f 'app-server daemon pid-update-loop'rm -rf ~/.codex/packages/app-server-daemon“With the flag off, a new TUI session no longer spawns a daemon,” the author reports. So containment is not the only lever; there is a working opt-out, at least at 0.157.0 on Linux/WSL2 with an npm install. What is not disableable is narrower: the updater’s force-kill of active turns. The mechanism is verified in #40969’s source review, but the disable-auto-update toggle proposed in that thread is not confirmed merged, so that limitation is dated to the source check.
The same issue links a cluster of package-manager friction reports worth knowing about before you decide how to install: #41188 on supporting externally managed daemon executables, #46468, where an npm update left the app-server on an old version while daemon update/restart “refuse recovery”, and #48050, where 0.157.0 would not start with the Arch Linux package unless --no-daemon was used. The --no-daemon flag exists, then, but it is the workaround, not the blocker: per that report, what fails on Arch is the default daemon auto-start path, and a flag that must be re-passed on every invocation, “including resume or fork and its arguments”, is a poor substitute for an opt-out that persists.
The two documented failure modes worth testing first
Even if you decide to keep the daemon, two reported behaviors can silently break scripted or proxied environments. Both are author-reported and unreplicated, so test them on your setup rather than taking them as universal.
Pinned environment. The daemon captures the environment of whichever session happened to start it. The reporter’s example:
I launch Codex through a wrapper that sets HTTPS_PROXY per invocation; the daemon keeps the first session’s proxy variables and its outbound connections go through that proxy, whatever later sessions are launched with.
If you route different sessions through different proxies, per-project credentials in environment variables, or anything else set per invocation, a long-lived daemon that froze the first session’s environment is a correctness problem, not just an aesthetic one. The bounded fix is to control which session starts the daemon, or to opt out.
Hook working directory. In --remote mode, hooks receive the daemon’s working directory instead of the session’s, per the same report. The issue links this to #42457 and #37537 as a likely shared root cause. Any hook that resolves relative paths, reads project config, or shells out assuming it runs in the project directory can misbehave here in ways that look like flaky hooks rather than a daemon problem.
There is independent reason to take these failure modes seriously rather than dismiss them as one person’s edge case. An FSE’26 empirical study of user-reported bugs in Claude Code, Codex, and Gemini CLI found failures concentrated in exactly the layers a shared background daemon occupies: “Tool / API Orchestration is the most affected layer, accounting for 37.6% of studied bugs”, with Command Execution and Monitoring second at 25% (study). The study maps where reported bugs live in these tools generally, not daemon-caused bugs specifically. But a persistent process that intermediates between your session and the tool layer is architecturally the place where environment and context mismatches live.
Managed-daemon default vs package-manager expectations
The decision here is really about who controls updates on a machine you manage. Codex CLI is distributed through npm, Homebrew, and standalone installers, channels whose users generally expect the package manager to be the update mechanism.
| Concern | Package-manager install expectation | Reported managed-daemon behavior (0.157.0, npm, WSL2) | With daemon_auto_start disabled |
|---|---|---|---|
| Which binary runs | The installed, pinned version | Private copy under ~/.codex/packages/.../releases/<version>/ | Not reported |
| Update timing | On your npm update schedule | First check 5 min after start, then hourly, self-applied | Not reported |
| Process lifetime | Dies with the session | Reparented to PID 1, survives closed windows until daemon stop or reboot | No daemon spawned by a new TUI session (the one reported outcome) |
| Mid-session stability | Restarts only when you restart | Updater “force-kills active turns after a 60s drain budget” (per #40969; drain budget verified in source, disable toggle unmerged) | Not reported |
| Environment/cwd semantics | Per-invocation | Daemon pins first session’s env; hooks may get daemon’s cwd | Not reported |
The report establishes exactly one outcome for the disabled-flag configuration: no daemon from a new TUI session. Which binary then executes, whether the updater is gone, and whether environment and cwd semantics revert are untested in the source, so verify on your install rather than reading that column as a guaranteed safe state.
OpenAI’s own documentation framing markets the CLI around user control: “Choose the model, reasoning effort, permissions, and commands that fit the task.” The CLI documentation page does not mention the daemon or updater (page retrieved 2026-10-12). That absence is not proof no documentation exists, but the contrast between the control framing and an undocumented self-updating default is worth naming.
There is also a useful precedent for why “audit what executes, not what you installed” is the right frame. Independent benchmark auditing of a coding-agent product, RuBench (arXiv v2), caught in Round 1 of RuBench 1.0 “the product silently substituting the model: on 5 of 25 tasks (20%) an official safeguard fallback re-routed routine HTTP-protocol fixes to Opus 4.8”. Different mechanism, same lesson: the deployed product layer, not the nominal configuration, is the unit that actually runs.
What remains unverified, and a re-verification routine
The limits here are substantial and should shape how much weight you give any of it:
- One report, one platform. Every core behavior above comes from a single author-reported, unreplicated issue on Linux/WSL2 at 0.157.0. macOS and Windows behavior is unknown.
- The line has moved. The releases page shows the stable line at 0.162.1 with 0.163.0 pre-releases, several versions past where the reported behavior began. The machinery persists: 0.162.1 “Fixed startup failures caused by differences between a running background server’s feature settings and CLI defaults” (#52648). But the defaults are moving too: #50555 skips daemon auto-start for Windows-mounted WSL homes, and #50559 distinguishes daemon release identity from executable contents. Flags, paths, and cadences in this article may already differ from your installed version.
- OpenAI’s rationale and response are absent. As of the October 12 snapshot, #48195 shows no maintainer reply, and there is no verified answer on whether the updater can be disabled independently of auto-start.
That last point is why the durable advice is a routine rather than a recipe. After every Codex CLI upgrade, re-run the three audit checks (pgrep -af 'app-server', the ls of the releases directory, pgrep -af 'pid-update-loop'), confirm your daemon_auto_start setting survived, and if you rely on proxy wrappers or hooks, re-test those two failure modes against the new version.
My recommendation, bounded by the evidence: if you run Codex CLI on a pinned, audited fleet or behind per-session proxy configuration, disable daemon_auto_start now and re-verify after each release. If you are a solo user who wants automatic updates anyway, the daemon may be a fair trade, but run the audit once so the trade is a choice rather than a surprise. Either way, the assumption that the npm-installed binary is the binary that runs no longer holds by default, and until OpenAI documents the intended behavior, verification is the only version-proof answer.

Join the discussion
Share a useful perspective or ask a question about this article.