# Codex CLI để lại daemon chạy ngầm: bốn coding agent được kiểm tra

[BigIron AI](https://homus.dev/speakers/bigiron-ai) · [BigIron AI](https://homus.dev/orgs/bigiron-ai)

> Bốn coding CLI để lại gì sau khi đóng terminal: daemon và updater của Codex, orphan của Gemini CLI, disk, kết nối, cách tắt và script kiểm tra.

Topics: Security, Coding Agents, Dev Tools

Canonical: https://homus.dev/talks/codex-cli-leaves-a-daemon-running-three-other-agents-tested-too

## 1. Đóng terminal rồi, agent đã thật sự tắt chưa?

BigIron AI mở đầu bằng một câu hỏi: bạn đóng terminal, và coding agent của bạn biến mất. Hay là chưa? Theo BigIron AI (video đăng ngày 20/10/2026), kể từ version 0.157, Codex khởi động một background server ở mỗi lần mở chế độ interactive, và server này tiếp tục chạy sau khi mọi cửa sổ Codex đã đóng.

![Slide mở đầu: What agents leave running, After You Close It, Four AI coding CLIs, audited](https://homus.dev/photos/lYmtoHk6Hg4-0003.jpg)

Slide mở đầu ghi "After You Close It" với dòng phụ "Four AI coding CLIs, audited": bốn coding CLI được kiểm tra xem còn để lại gì sau khi cửa sổ đóng (video đăng ngày 20/10/2026).

Bài guide của Big Iron đã kiểm tra chuyện đó, và kiểm tra luôn ba terminal agent khác, trên một máy ảo Debian mới tinh. BigIron AI đi qua từng tool: nó còn để lại thứ gì đang chạy, nó ghi gì xuống disk, và nó gọi về đâu (phoned home). Mọi con số dưới đây là số đo trên máy ảo test được mô tả trong video đăng ngày 20/10/2026.

## 2. Bản ngắn gọn: bốn tool để lại gì

Bản ngắn gọn, theo số đo trong video đăng ngày 20/10/2026: Codex để lại hai process đang chạy, khoảng 370 megabyte, cùng một updater lên mạng năm phút sau khi khởi động. Gemini CLI cũng để lại hai process, khoảng 400 megabyte, và theo BigIron AI thì trường hợp này trông giống một bug.

![Slide The Short Version với bốn gạch đầu dòng về Codex, Gemini CLI, Claude Code và opencode](https://homus.dev/photos/lYmtoHk6Hg4-0043.jpg)

Slide "The Short Version": Codex 2 process, khoảng 370 MB, có self-updater; Gemini CLI 2 process mồ côi (orphaned), khoảng 400 MB; Claude Code và opencode biến mất trong vòng 15 giây; không tool nào cài systemd unit, cron job hay autostart (số đo trong video đăng ngày 20/10/2026).

Claude Code và OpenCode thì thoát trong vòng 15 giây ở mọi lần chạy, và không gửi gì ra ngoài sau đó (số đo trong video đăng ngày 20/10/2026). Ngoài ra, không tool nào trong bốn tool cài một systemd unit, một cron job hay một mục auto-start, nên một lần reboot sẽ chấm dứt tất cả.

## 3. Vì sao đáng để ý trên homelab: uptime và máy dùng chung

Vậy tại sao phải bận tâm trên một homelab? Câu trả lời của BigIron AI (video đăng ngày 20/10/2026) là uptime. Một dev VM hay một jump host có thể chạy hàng tháng trời giữa hai lần reboot. Mỗi lần mở Codex ở chế độ interactive khi daemon chưa chạy sẽ khởi động một daemon, và mỗi session Gemini bị rớt sẽ để lại một cặp process.

Trên một máy dùng chung, theo số đo trong video đăng ngày 20/10/2026, mỗi user có daemon riêng của mình và bản copy riêng 432 megabyte của package Codex. Slide đi kèm ghi "432 MiB, a Codex package copy per user", kèm dòng "plus a daemon per user, on a shared box".

Trên một máy dùng chung, theo số đo trong video đăng ngày 20/10/2026, mỗi user dùng Codex có daemon riêng và bản copy package riêng khoảng 432 MiB; các process này sống cho tới lần reboot kế tiếp.

## 4. Bài test được chạy thế nào

Trước hết là cách chạy test, vì theo BigIron AI, chính các chi tiết quyết định kết quả có ý nghĩa gì. Máy test là một máy ảo Debian 13 dùng xong bỏ (throwaway) trên Proxmox, với bốn virtual CPU và sáu gigabyte RAM. Mỗi tool được cài theo cách vendor của nó khuyến nghị: Codex 0.162.1 và Gemini 0.63 qua npm; Claude Code 2.1.296 và opencode 1.19 qua install script của chúng. Mỗi tool có một Linux user mới riêng (thiết lập test trong video đăng ngày 20/10/2026).

Về model, Ollama chạy ngay trên máy ảo, serve model qwen3:1.7b (slide ghi Ollama 0.40.3). Gemini không nói chuyện được với Ollama, nên nó được cấp một API key cố tình không hợp lệ; slide ghi "Gemini CLI: an invalid key, prompt fails" (thiết lập test trong video đăng ngày 20/10/2026).

Quy trình như sau: khởi động agent bên trong tmux, chờ 25 giây, gõ một prompt, chờ 90 giây, rồi kill tmux server. Thao tác đó đóng terminal, y như khi bạn đóng cửa sổ hoặc khi một session SSH bị rớt: kernel gửi tín hiệu hang-up (SIGHUP). Sau đó kiểm tra process ở giây thứ 15 và mỗi phút một lần, tối đa 10 phút. Mọi kết nối outbound được log theo từng user bằng một firewall rule, và home directory được diff (quy trình trong video đăng ngày 20/10/2026).

![Cửa sổ terminal One run, per tool: tmux new, chờ, prompt Reply with the single word OK, tmux kill-server](https://homus.dev/photos/lYmtoHk6Hg4-0223.jpg)

"One run, per tool": khởi động agent bằng `tmux new -d -s s 'cd ~/proj && codex'`, chờ 25 giây, gửi một prompt "Reply with the single word OK.", chờ 90 giây, rồi `tmux kill-server` để đóng pty bằng SIGHUP; sau đó kiểm tra process ở +15 giây rồi mỗi phút, nftables log kết nối outbound theo UID, cuối cùng diff home directory (video đăng ngày 20/10/2026).

BigIron AI nêu hai lưu ý thẳng thắn (honest caveats) từ bài guide. Thứ nhất, một model nhỏ chạy chậm trên bốn core, nên ở vài lần chạy terminal bị đóng khi model vẫn đang trả lời; theo BigIron AI, đó là một bài test khó hơn. Thứ hai, một lỗi của dàn test ở giai đoạn đầu khiến session Codex và Claude Code đầu tiên không nhận được câu trả lời nào, nên cả hai được chạy lại với một back-end hoạt động bình thường. Những gì còn sót lại thì giống hệt (số đo trong video đăng ngày 20/10/2026).

## 5. Codex: daemon là mặc định, không phải bug

Codex được nói tới đầu tiên, vì theo BigIron AI (video đăng ngày 20/10/2026), hành vi của nó không phải bug. Đó là mặc định, và lý do OpenAI đưa ra là nó cho phép nhiều client, bao gồm desktop app và remote control, dùng chung một back-end.

Trong vòng ba giây sau khi mở, Codex sinh ra thêm hai process (số đo trong video đăng ngày 20/10/2026). Một là app server, khởi động dưới dạng managed daemon. Hai là một updater riêng tên là `pid-update-loop`. Cả hai chạy từ một bản copy riêng của binary, nằm trong thư mục `packages` bên trong `.codex` ở home directory. Mỗi process không có terminal, và mỗi process tự làm leader của session riêng.

![Cây process của Codex ba giây sau khi khởi động, với app-server --managed-daemon và app-server daemon pid-update-loop](https://homus.dev/photos/lYmtoHk6Hg4-0323.jpg)

"Codex, 3 seconds after launch": từ `node /usr/local/bin/codex` đi xuống binary trong `vendor`, rồi hai process chạy từ `~/.codex/packages/.../bin/codex`: `app-server --managed-daemon` và `app-server daemon pid-update-loop`; dòng chú thích ghi hai process cuối không có terminal và mỗi cái có session riêng (video đăng ngày 20/10/2026).

15 giây sau khi terminal đóng, các process do npm cài đã biến mất, còn hai process này đã được PID 1 nhận nuôi (adopted). Mười phút sau, cả hai vẫn đang chạy, với 257 MB và 116 MB resident; slide ghi "~370 MB still resident after 10 minutes" (số đo trong video đăng ngày 20/10/2026). Không có gì trong cron, systemd user unit hay autostart trỏ tới chúng, và user đó không bật lingering. Vậy nên một lần reboot sẽ kết thúc chúng. Còn trước lúc đó, chúng ở lại.

## 6. Codex giữ những gì, và gọi ra ngoài lúc nào

Chúng giữ mở những gì? Theo số đo trong video đăng ngày 20/10/2026, app server lắng nghe trên một Unix socket dưới `/tmp`; slide ghi `/tmp/codex-daemon-UID/hash`. Updater lắng nghe trên socket riêng của nó bên trong thư mục `.codex` (slide ghi `~/.codex/.../daemon-updater.sock`). Và trong lúc chính session Codex interactive còn mở, nó còn lắng nghe trên một TCP port ngẫu nhiên ở địa chỉ loopback. Chỉ loopback thôi, nhưng theo BigIron AI thì đáng biết trên một máy nhiều user.

Tiếp theo là network trace, theo số đo trong video đăng ngày 20/10/2026. Lúc khởi động có kết nối tới GitHub, chatgpt.com và npm registry. Thêm một kết nối ở giây thứ 62. Rồi tới giây thứ 303, khi terminal đã đóng hơn ba phút, có kết nối tới chatgpt.com và releases.openai.com. Lần chạy lại khớp tới từng giây.

![Danh sách kết nối outbound của Codex theo thời gian từ lúc khởi động](https://homus.dev/photos/lYmtoHk6Hg4-0423.jpg)

"Outbound, by time from launch": từ +2 đến +4 giây là github.com, api.github.com, raw.githubusercontent.com, chatgpt.com, ab.chatgpt.com, registry.npmjs.org; +62 giây là chatgpt.com; +303 giây là chatgpt.com và releases.openai.com khi terminal đã đóng hơn ba phút; hai dòng cuối ghi `INITIAL_UPDATE_DELAY = 5 min` và `DEFAULT_UPDATE_INTERVAL_MINUTES = 60` (số đo trong video đăng ngày 20/10/2026).

Theo BigIron AI (video đăng ngày 20/10/2026), mốc thời gian đó nằm sẵn trong source code: một khoảng trễ update ban đầu 5 phút, rồi cứ 60 phút kiểm tra một lần, chừng nào máy còn chạy. Khi tìm thấy một release, nó tải release đó về home directory của bạn và khởi động lại back-end. Theo BigIron AI, chuyện tự update này quan trọng nếu bạn pin version: sau khi daemon tự update, back-end đang chạy các session của bạn là thứ nó đã tải về, không phải thứ npm đã cài.

## 7. 541 MB trên disk, và khi nào Codex bỏ qua daemon

Còn disk. Home directory của user test đi từ 4,6 kilobyte lên 541 megabyte sau một session, phần lớn là một bản copy đầy đủ của package và một bản clone plugin (số đo trong video đăng ngày 20/10/2026). Slide "541 MB, Codex home dir, after one session" ghi rõ: bản copy package 432 MiB cộng bản clone plugin 103 MB.

Khi nào Codex bỏ qua daemon? Theo BigIron AI (video đăng ngày 20/10/2026), source code liệt kê các điều kiện: chọn một profile, một override, hoặc đặt một exec server URL trong biến môi trường. Bài guide (đọc ngày 26/10/2026) ghi cụ thể đó là `--profile`, override config bằng `-c` và biến `CODEX_EXEC_SERVER_URL`.

Bài guide đã kiểm tra các kiểu khởi động sau, theo kết quả trong video đăng ngày 20/10/2026. Một lần khởi động interactive bình thường thì khởi động daemon. Cờ `--oss`, cờ `--no-daemon`, chế độ non-interactive `exec`, và việc tắt feature flag thì đều không. Danh sách features cũng cho thấy Daemon Auto Start ở trạng thái stable, và bật theo mặc định.

![Bảng Does it start the daemon cho năm kiểu khởi động Codex và output codex features list](https://homus.dev/photos/lYmtoHk6Hg4-0523.jpg)

"Does it start the daemon?": `codex` là yes; `codex --oss`, `codex --no-daemon`, `codex exec "..."` và `codex` sau khi features disable đều là no; lệnh `codex features list` in ra `daemon_auto_start stable true` (kết quả trên Codex 0.162.1, video đăng ngày 20/10/2026).

## 8. Dừng daemon: updater vẫn ở lại

Dừng nó thì có một cái bẫy (a catch), được báo lần đầu trong issue upstream và được bài guide tái hiện y hệt (theo video đăng ngày 20/10/2026). Lệnh daemon stop chờ 60 giây, đúng bằng thời gian shutdown grace mặc định, rồi ép server tắt. Ở một lần chạy sau, khi không có turn nào đang dở, lệnh trả về trong khoảng một giây.

![Lệnh codex app-server daemon stop và output ps cho thấy pid-update-loop vẫn chạy 70 giây sau](https://homus.dev/photos/lYmtoHk6Hg4-0603.jpg)

"daemon stop leaves the updater": `codex app-server daemon stop` in ra `shutdown_requested`, rồi `shutdown_forced` 60 giây sau, rồi `"status":"stopped"`; 70 giây sau, `ps -u cx2 -o pid,etimes,args` vẫn thấy `.../codex app-server daemon pid-update-loop` (số đo trong video đăng ngày 20/10/2026).

Nhưng 70 giây sau lệnh stop, updater vẫn đang chạy (số đo trong video đăng ngày 20/10/2026). Nó phải bị kill riêng.

## 9. Gemini CLI: orphan không chịu hang up

Gemini CLI thì khác. Theo BigIron AI (video đăng ngày 20/10/2026), không có gì ở đây là do thiết kế. Nó đơn giản là không hang up. Gemini chạy thành hai process node: một parent nhỏ, và một child được relaunch với heap lớn hơn.

Khi terminal đóng, cả hai tiếp tục chạy. Mười phút sau chúng vẫn còn đó, tổng cộng khoảng 400 megabyte, ở trạng thái idle, với hai giây CPU time (số đo trong video đăng ngày 20/10/2026). Input và output của chúng trỏ tới một terminal đã bị xoá. Và process status cho thấy cả hai đều bắt (catch) tín hiệu hang-up, thay vì làm hành động mặc định là thoát.

![Output ps của Gemini mười phút sau khi đóng: hai process node, parent có PPID 1](https://homus.dev/photos/lYmtoHk6Hg4-0643.jpg)

"Gemini, 10 minutes after close": process 9150 (`node .../gemini`) có PPID 1, process con 9158 chạy với `--max-old-space-size=2965`, cột TT là `?`; hai dòng chú thích ghi stdin/out/err trỏ tới `/dev/pts/0 (deleted)` và cả hai bắt SIGHUP thay vì thoát (số đo trong video đăng ngày 20/10/2026).

Theo số đo trong video đăng ngày 20/10/2026, mọi lần chạy Gemini không có bản sửa (fix) mô tả ở phần sau đều tái hiện được lỗi này, kể cả một lần qua một session SSH thật, không gõ prompt nào, và client bị kill. Cả hai process vẫn còn đó 24 phút sau. Slide "Reproduced Every Time" tóm lại: mọi lần chạy không có fix, kể cả SSH; vẫn chạy 24 phút sau qua SSH; issue upstream gemini-cli #25590 vẫn mở; thói quen an toàn lúc này là thoát bằng /quit.

Report upstream gần nhất là Gemini CLI issue 25590, mà video (đăng ngày 20/10/2026) nói là vẫn đang mở: wrapper relaunch không chuyển tiếp các tín hiệu termination xuống process con. Khi đọc ngày 26/10/2026, trang issue [google-gemini/gemini-cli#25590](https://github.com/google-gemini/gemini-cli/issues/25590) có tiêu đề "relaunchAppInChildProcess does not forward signals to child, orphaning it when parent is killed" và ở trạng thái closed. Lời khuyên trong video: cho tới khi được sửa, hãy thoát từ bên trong bằng `/quit`.

## 10. Claude Code và opencode: thoát sạch, nhưng vẫn có giá

Hai tool còn lại thì cư xử đúng mực. Đây là cái giá chúng vẫn tốn, theo số đo trong video đăng ngày 20/10/2026.

Claude Code chạy thành một process duy nhất, khoảng 265 tới 340 megabyte resident. 15 giây sau khi terminal đóng, không còn gì sót lại ở cả ba lần chạy, kể cả một lần đóng giữa chừng một turn. Lúc khởi động, nó liên lạc với Anthropic API và server tải về của nó (slide ghi api.anthropic.com và downloads.claude.ai), ngay cả khi model chạy trên Ollama local, và hoàn toàn không gửi gì sau khi thoát. Slide "Claude Code 2.1.296" còn ghi disk cho mỗi session là 15 đến 19 MB, chủ yếu là plugin (số đo trong video đăng ngày 20/10/2026).

Theo BigIron AI (video đăng ngày 20/10/2026), các release cũ hơn từng có bug orphan trên issue tracker của chính Claude Code, nhưng ở version này, bài guide không tái hiện được.

OpenCode nặng nhất khi đang chạy, khoảng 800 megabyte cho một process, và sạch nhất sau đó: không còn gì sau 15 giây, và không có kết nối nào (số đo trong video đăng ngày 20/10/2026). Phần nó để lại nằm trên disk. Bản cài đặt ghi 178 megabyte vào home directory, và một session thêm 214 megabyte nữa: một npm cache, một provider package được cài ở lần dùng đầu tiên, các runtime dependency và model catalog.

![Slide opencode 1.19.0 với bốn gạch đầu dòng về RAM và disk](https://homus.dev/photos/lYmtoHk6Hg4-0803.jpg)

Slide "opencode 1.19.0": khoảng 800 MB khi chạy, không còn gì sau đó; bản cài 178 MB trong home directory; một session thêm 214 MB; npm cache 98 MB, provider package 55 MB (số đo trong video đăng ngày 20/10/2026).

Theo BigIron AI (video đăng ngày 20/10/2026), trên một máy không có internet, hãy tính trước cho lần khởi động đầu tiên đó.

## 11. Tắt bớt kết nối, và tắt hẳn daemon của Codex

Vậy làm sao vặn nhỏ từng tool lại? Theo BigIron AI (video đăng ngày 20/10/2026), mỗi tool có một switch được document cho việc kiểm tra update và telemetry. Bài guide khởi động ba tool lần thứ hai, có và không có switch đó, rồi đếm số kết nối outbound mới trong phút tiếp theo.

Theo số đo trong video đăng ngày 20/10/2026: Claude Code đi từ hai xuống không, với biến môi trường mà video đọc là Claude Code Disable Non-Essential Traffic đặt bằng 1 (bài guide, đọc ngày 26/10/2026, ghi tên biến là `CLAUDE_CODE_DISABLE_NONESSENTIAL_TRAFFIC=1`). Gemini đi từ bốn xuống một, chính là prompt, với ba setting trong settings file của nó. OpenCode đi từ 3 xuống 0 với hai biến môi trường. Không setting nào trong số này thay đổi chuyện có process nào bị bỏ lại hay không.

![Slide Connections in the Next Minute: Claude Code 2 to 0, Gemini CLI 4 to 1, opencode 3 to 0](https://homus.dev/photos/lYmtoHk6Hg4-0843.jpg)

Slide "Connections in the Next Minute": Claude Code 2 xuống 0, Gemini CLI 4 xuống 1 (chính là prompt), opencode 3 xuống 0, và dòng cuối "None of these stops a leftover process" (số đo trong video đăng ngày 20/10/2026).

Với daemon của Codex, switch là Daemon Auto Start (theo video đăng ngày 20/10/2026). Script của bài guide tắt nó, dừng daemon, kill updater, rồi xoá cả bản copy package lẫn state của daemon; nếu không xoá cả hai thì một lần bật lại sau này sẽ fail. Bốn lệnh cốt lõi của script `codex-daemon-off.sh` hiện trên màn hình:

```
codex features disable daemon_auto_start
codex app-server daemon stop
pkill -u "$(id -u)" \
-f '[a]pp-server daemon pid-update-loop'
rm -rf ~/.codex/packages/app-server-daemon \
~/.codex/app-server-daemon
```

Theo số đo trong video đăng ngày 20/10/2026, script chạy trong 1,3 giây và để lại 109 megabyte trong `~/.codex`. Lần khởi động tiếp theo không khởi động daemon nào.

## 12. Để logind dọn dẹp

Với Gemini, hay với bất cứ thứ gì khác, BigIron AI (video đăng ngày 20/10/2026) gợi ý để logind dọn dẹp. Một setting drop-in `KillUserProcesses=yes` khiến nó kết thúc mọi thứ mà một login session đã khởi động. Qua SSH, 20 giây sau khi ngắt kết nối, không còn process nào của Gemini, cũng không còn daemon và updater của Codex (số đo trong video đăng ngày 20/10/2026).

![Slide Let logind Clean Up với bốn gạch đầu dòng về KillUserProcesses](https://homus.dev/photos/lYmtoHk6Hg4-0943.jpg)

Slide "Let logind Clean Up": `KillUserProcesses=yes` trong một logind drop-in; qua SSH, cả hai tool biến mất 20 giây sau khi ngắt kết nối; đánh đổi là tmux và screen bị kill khi logout; nó chỉ tác động khi login session kết thúc (số đo trong video đăng ngày 20/10/2026).

Cái giá phải trả, theo BigIron AI (video đăng ngày 20/10/2026): nó cũng kill các session tmux và screen khi bạn logout. Bài guide nêu hai cách tránh, một là loại trừ những user được chỉ định, hai là chạy các session dài dưới systemd manager riêng của user, nhưng không test hai cách đó. Và nó chỉ tác động khi session kết thúc.

## 13. Kiểm tra máy của chính bạn

Cuối cùng, kiểm tra máy của chính bạn. Theo video đăng ngày 20/10/2026, script read-only của bài guide liệt kê các process agent không có terminal, các socket chúng đang lắng nghe, các hook auto-start, và dung lượng disk mà state của agent đang dùng. Màn hình cho thấy kết quả với user Codex, 10 phút sau khi đóng.

![Output agent-leftovers.sh cho user Codex: hai process không có terminal, một Unix socket, không autostart hook, disk 540M](https://homus.dev/photos/lYmtoHk6Hg4-1023.jpg)

"agent-leftovers.sh, Codex user": mục "Processes with no terminal" có hai process `.../codex app-server ...` và `.../codex ... pid-update-loop` đã chạy 503 giây; một Unix socket dưới `/tmp/codex-daemon-1008/`; mục Autostart hooks trống; disk 540M cho `~/.codex` và 432M cho `~/.codex/packages` (kết quả trong video đăng ngày 20/10/2026).

BigIron AI gợi ý chạy script đó từ cron hoặc từ một check monitoring (video đăng ngày 20/10/2026). Mục đầu tiên không rỗng chính là tín hiệu.

Lời kết của BigIron AI (video đăng ngày 20/10/2026): tắt daemon của Codex trên các máy dùng chung và trên bất cứ máy nào bạn SSH vào, để logind dọn các session, tính trước dung lượng disk, và thoát agent bằng lệnh quit của chính nó thay vì đóng cửa sổ.

## Nguồn và liên kết

- Tên gốc: Codex CLI Leaves a Daemon Running. Three Other Agents, Tested Too
- Sự kiện: [YouTube](https://homus.dev/events/youtube)
- Video gốc: [https://www.youtube.com/watch?v=lYmtoHk6Hg4](https://www.youtube.com/watch?v=lYmtoHk6Hg4)

- Kênh YouTube BigIron AI (BigIron AI): [youtube.com/channel/UCHvsCXKsRnm_uRXfq72xZkA](https://www.youtube.com/channel/UCHvsCXKsRnm_uRXfq72xZkA)

- Bài guide của Big Iron, nguồn của các số đo (đọc ngày 26/10/2026): [What AI Coding CLIs Leave Running: Codex's Background Daemon, Audited](https://www.bigiron.cc/guides/what-ai-coding-clis-leave-running-codexs-background-daemon-audited)

- Issue Gemini CLI về wrapper relaunch không chuyển tiếp tín hiệu xuống process con (trạng thái closed khi đọc ngày 26/10/2026): [google-gemini/gemini-cli#25590](https://github.com/google-gemini/gemini-cli/issues/25590)

Nguồn: https://homus.dev/talks/codex-cli-leaves-a-daemon-running-three-other-agents-tested-too · cập nhật 2026-10-26
