如何在 Linux VPS 使用 tmux 執行 Claude Code
避免 SSH 連線中斷導致 Claude Code 停止運作。透過在 Linux VPS 上使用 tmux 建立持久化 session,確保 Agent 在斷線後仍能持續執行任務,並提供完整的安裝與配置指南。
問題在於筆電螢幕蓋,而非 CLI
Claude Code 在筆電上運作正常,直到你闔上螢幕:SSH 連線會中斷,shell 會收到 SIGHUP,導致 agent 在測試執行三分鐘後隨之停止。請在永不進入睡眠狀態的機器上執行 CLI,並使用 terminal multiplexer,確保其進程不是 SSH session 的子進程。這就是關鍵所在 —— 核心技術是 tmux,而非安裝過程。
本頁面說明如何操作用來執行 agent 的主機。如果你沒有可以保持開機狀態的 Linux 伺服器,則本指南不適用。這是唯一的必要前提。
tmux 的實際運作機制
當您透過 SSH 連線時,sshd 會派生 (fork) 一個 shell 並分配一個偽終端機 (pseudo-terminal);從該 shell 啟動的所有程序皆為其子程序。若連線中斷,核心 (kernel) 會銷毀 pty,導致 shell 收到 SIGHUP,進而使其子程序也隨之結束。這會導致長時間執行的前景程序停止運作。
tmux 則反轉了這種隸屬關係。您輸入的 tmux 指令是一個輕量級用戶端,透過 unix socket 與脫離終端機運行的 tmux server 通訊。Session 內的 shell 是該 server 的子程序,而非 sshd 的子程序。即使 SSH 連線斷開,用戶端會結束,但 server、session 與執行中的 agent 仍會持續運行。重新連線並執行 tmux attach,您即可回到原有的 shell 並保留相同的捲動紀錄 (scrollback)。nohup 也能在斷線後存活,但無法讓您重新進入背景運行的 TUI。Claude Code 具備互動性;tmux (或 screen) 才是正確的工具。
規格配置
CLI 本身是 Node 處理程序,並不會佔用大量資源。真正消耗資源的是代理程式(agent)所執行的任務:例如 build、完整的測試套件、tsc、language server 或 Docker 中的資料庫。請針對工具鏈(toolchain)進行規格配置,而非針對 CLI。即使不打算使用 swap,也請配置 swap —— 這能將嚴重的 OOM kill 轉化為較慢的 build 過程:
sudo fallocate -l 4G /swapfile
sudo chmod 600 /swapfile
sudo mkswap /swapfile
sudo swapon /swapfile
echo '/swapfile none swap sw 0 0' | sudo tee -a /etc/fstab同時請注意磁碟空間:repos、node_modules 與 Docker images 會迅速累積。若工具鏈的範圍超出容器,延伸至完整的虛擬機器(例如 KVM guest 或本地 Kubernetes 節點),請在部署前確認方案是否支援 CPU 虛擬化擴充指令集,因為 在 VPS 上執行嵌套虛擬化 是由供應商開啟,而非從 guest 內部自行切換。
使用非 root 使用者
建立一個擁有獨立 home 目錄的專用使用者,並配置好您的 public key:
sudo adduser --disabled-password --gecos "" agent
sudo install -d -m 700 -o agent -g agent /home/agent/.ssh
sudo cp ~/.ssh/authorized_keys /home/agent/.ssh/authorized_keys
sudo chown agent:agent /home/agent/.ssh/authorized_keys
sudo chmod 600 /home/agent/.ssh/authorized_keys刻意地,agent 並不在 sudo 群組中。若需要系統套件,請自行安裝。這項決策可以防止隨機執行的 shell 指令破壞主機。
針對長期運行的主機進行 SSH 維護
對於長期暴露於公網、且存有 agent 與原始碼的主機,使用密碼驗證(Password auth)是不必要的風險。請將其關閉。在 Ubuntu 24.04 與 Debian 13 中,/etc/ssh/sshd_config 包含 /etc/ssh/sshd_config.d/*.conf,因此建議直接新增設定檔,而非修改主設定檔:
# /etc/ssh/sshd_config.d/10-hardening.conf
PasswordAuthentication no
KbdInteractiveAuthentication no
PermitRootLogin no驗證並重新載入 —— 在測試新連線時,請保持目前的連線開啟:
sudo sshd -t && sudo systemctl restart sshUbuntu 24.04 的細節差異:sshd 是透過 socket 啟動的。驗證設定適用於 systemctl restart ssh,但若變更監聽的 Port,則需要 systemctl daemon-reload 並重新啟動 ssh.socket。
接著是防火牆。在啟用 SSH 之前,請務必先允許 SSH 流量,否則會導致無法連線:
sudo ufw allow OpenSSH
sudo ufw default deny incoming
sudo ufw default allow outgoing
sudo ufw enable安裝 fail2ban 時請評估其效益:一旦關閉密碼驗證,暴力破解(brute force)將無法成功 —— 該工具能避免失敗的嘗試填滿你的 journal。
# /etc/fail2ban/jail.local
[sshd]
enabled = true
backend = systemd
maxretry = 5
bantime = 1h最後,使用 sudo apt install unattended-upgrades 與 sudo dpkg-reconfigure -plow unattended-upgrades 進行自動更新。請注意與 tmux 的互動:若開啟 Unattended-Upgrade::Automatic-Reboot 且核心(kernel)更新導致主機重啟,所有連線都會中斷。若將其關閉,則可在任務執行結束後再自行重啟。
在 Ubuntu 上安裝 Node.js 與 Claude Code
Claude Code 為 Node CLI,因此需要安裝最新的 Node 版本。發行版套件庫的版本通常較舊;在 Ubuntu 與 Debian 上,通常使用 NodeSource,它提供已簽署的套件庫(不含 apt-key — 該工具已移除):
curl -fsSL https://deb.nodesource.com/setup_24.x | sudo -E bash -
sudo apt install -y nodejs
node --version接下來是常見錯誤:請以 agent 使用者身分安裝 CLI,絕不要使用 sudo npm -g。 使用 root 權限的 global prefix 會導致後續出現權限錯誤,並在 npm cache 中留下 root 權限的檔案。請先將 npm 的 prefix 指向使用者的 home 目錄:
mkdir -p ~/.npm-global
npm config set prefix ~/.npm-global
echo 'export PATH="$HOME/.npm-global/bin:$PATH"' >> ~/.bashrc
source ~/.bashrc
npm install -g @anthropic-ai/claude-code
claude --versionexport 指令應寫在 ~/.bashrc 而非 ~/.profile,且必須放在檔案開頭「If not running interactively, don't do anything」判斷式之上方:因為 tmux 可能會啟動 non-login shells,這會讀取 ~/.bashrc 並跳過 ~/.profile — ~/.profile 僅在 login shells 執行。透過 nvm 等版本管理工具安裝 per-user Node 可達到相同效果;無論採用哪種方式,目標都是讓 npm install -g 不需要 sudo。npm 仍可正常運作,或者您也可以使用 Anthropic 目前文件建議的預設安裝指令碼。貼上指令前請先確認 Anthropic 的安裝文件 — 安裝方式會隨時變動。
在專案目錄內執行 claude 即可啟動。首次執行會引導您完成驗證程序;若是在 headless 主機上,由於沒有瀏覽器,流程會提供一個 URL 供您在自己的電腦開啟,並提供一個 code 讓您帶回終端機。(另一種方式是在環境變數中設定 API key。)無論使用哪種方式,憑證都會儲存在伺服器上 — 這就引出了大家常忽略的部分。
影響範圍 (Blast Radius) 討論
具備 shell 存取權限的 agent 即是一個 shell。它能讀取該使用者可讀取的任何內容,並能推送到該使用者可推送的任何地方。這並非針對工具本身的批評,而是其定義使然 —— 這也是為何執行該 agent 的帳戶權限,比任何個別設定都更重要的原因。
- 專用且低權限的使用者。 不可隸屬於
sudo群組,且不得與您的個人帳戶共用 home directory。 - 主機上不得存放正式環境憑證。 不要在
~/.aws/credentials中存放正式環境金鑰,不要從正式環境複製.env,也不要提供具備重要資料寫入權限的資料庫密碼。請為 agent 提供 staging 環境或唯讀的憑證。 - 範圍受限的 token。 使用僅限單一 repository 的細粒度 GitHub token;若僅需讀取權限,請使用 deploy key。
Claude Code 提供了一個可完全跳過權限提示的 flag。在筆記型電腦或暫時性的專案中使用,由您自行決定。但在存放 token 的伺服器上,該 flag 會移除最後一道防線,導致錯誤的指令直接導致 git push --force。關於該 flag 的實際影響,以及如何限制使用該 flag 運行的 agent(從內建的 sandbox 到拋棄式 VPS),請參閱 在伺服器上安全運行 Claude Code。
Deploy key 與 SSH agent forwarding 之比較
使用 ssh -A 以讓 git 能使用您筆記型電腦上的金鑰是很常見的做法。但請理解這代表什麼:agent forwarding 會將您本地 SSH agent 的 socket 暴露給該主機上以該使用者身份運行的程序。只要您保持連線,任何以 agent 身份運行的程序(包括 agent 本身)都可以要求您的金鑰為其可觸及的 任何 主機進行簽章。這遠遠超出了「僅允許 git pull 此 repository」的範圍。
請改為在伺服器上生成金鑰,將其註冊為單一 repository 的 deploy key(若 agent 需要 push,則僅給予寫入權限),並設定 git identity 以便辨識來自該主機的 commits:
ssh-keygen -t ed25519 -C "agent deploy key" -f ~/.ssh/id_ed25519_repo
cat ~/.ssh/id_ed25519_repo.pub # paste into the repo's Deploy Keys
git config --global user.name "Agent (build box)"
git config --global user.email "agent@example.com"tmux 工作流程
安裝 sudo apt install tmux,接著建立一個最小化 ~/.tmux.conf:
set -g mouse on
set -g history-limit 50000
set -g default-terminal "tmux-256color"以下四個指令可應付日常使用:
tmux new -A -s claude # attach to session "claude", creating it if absent
# ...run `claude` inside it, work normally...
# Ctrl-b then d -> detach; everything keeps running
tmux ls # list sessions
tmux attach -t claude # reattach, from this machine or any other
tmux kill-session -t claude應記住 tmux new -A -s claude — 若 session 已存在則進行 attach,若不存在則建立 session,因此一個指令即可完成啟動與恢復作業。建議設定 alias。在 session 內,Ctrl-b c 用於開啟 window,Ctrl-b n 與 Ctrl-b p 用於切換 window,Ctrl-b [ 進入 copy mode 以進行捲動回溯 (q 退出)。
關於永不關閉的 session 需注意:agent 會在每次回合重新傳送完整對話,因此在讓 session 運行一週之前,請先閱讀 長期運行的 Claude Code session 如何消耗 token。
Failure modes
「我的 session 消失了。」 tmux ls 顯示 no server running on /tmp/tmux-1000/default。這通常代表該 process 從未在 tmux 內執行 —— 您直接透過 SSH 登入並執行了 claude,隨後的連線中斷導致其終止。無法復原。預防方法:登入後的第一個指令應為 tmux new -A -s <project>。
窗格(pane)縮小成極小的方框。 tmux 會根據最小的已連接用戶端(client)來調整 session 大小,因此若有來自其他機器的舊連線仍處於連接狀態,會壓縮顯示畫面。連接時強制踢除其他連線:tmux attach -d -t claude。
編譯時顯示 Killed。 僅顯示單一字詞,無 stack trace。請使用 sudo dmesg -T | grep -i -E 'out of memory|killed process' 確認 —— 這是 kernel OOM killer 挑選了佔用資源最大的 process。若使用 Node,可能會看到 FATAL ERROR: Ineffective mark-compacts near heap limit Allocation failed - JavaScript heap out of memory。解決方案依序為:增加 swap(如上所述)、限制測試與編譯的並行度、使用 NODE_OPTIONS=--max-old-space-size=... 增加 Node heap,或升級 VPS 規格。OOM killer 也可能挑選 tmux server 而非編譯程序,導致 session 隨之消失;若 systemd-oomd 正在執行,它可能會殺掉整個 user slice 並產生相同結果。
npm error code EACCES / permission denied, mkdir '/usr/lib/node_modules/...'。 將套件安裝至 root 擁有的 prefix。請使用上述的 ~/.npm-global prefix。若您曾執行過 sudo npm,可能會看到 Your cache folder contains root-owned files —— 請使用 sudo chown -R $(id -u):$(id -g) ~/.npm 修復。
claude: command not found —— 但僅偶爾發生。 您的 PATH export 位在 ~/.bashrc 中,且位於「If not running interactively, don't do anything」防護機制下方,因此非互動式 shell 會跳過它。請將 export 移至該防護機制上方,並將其保留在 ~/.bashrc 而非 ~/.profile:tmux 可能會啟動非 login shell,這些 shell 會讀取 ~/.bashrc 但不會觸及 ~/.profile。
連接後顏色顯示異常。 發生 TERM 不匹配 —— 請參考上方 default-terminal 行的解決方案。
重新啟動後 session 消失。 這並非錯誤:tmux server 是一個 process,重新啟動會終止該程序。請檢查 uptime。
規模擴大後的潛在問題
專案數量增加。 建議為每個 repo 建立一個以該名稱命名的 tmux session;tmux ls 即為你的儀表板。若缺乏命名規範,會導致 session 0、1、2 混亂。Port 分配亦然 —— 當六個 repo 都需要使用 :3000 時,應停止手動分配,改用 Traefik reverse proxy 在 Docker Compose 下透過 hostname 進行多個應用程式的路由。
使用者人數增加。 tmux socket 是針對單一使用者設計的,因此同一台機器上的兩位開發者各自擁有獨立的 tmux server,無法看到對方的 session。透過共用 socket 來共享單一 session,意味著所有人都在同一個 Unix 使用者的 shell 中輸入指令,這會帶來審核與權限方面的問題。使用獨立使用者是最穩妥且正確的做法。
自動化工作。 tmux 適用於可進行 attach 的互動式 session。若工作是定期執行且無需人工監控,應將其納入 systemd unit 與 timer 管理,如此即可獲得日誌記錄、重啟策略以及開機自動啟動功能。若試圖用 tmux 來執行類似 cron 的工作,代表該工作應該被視為一個 service。
最後一點:開發伺服器應綁定在 agent 啟動的 127.0.0.1 而非 0.0.0.0,並透過 SSH tunnel (ssh -L 3000:127.0.0.1:3000 agent@your-server) 進行連線,而非在 ufw 中開啟 port。當你需要轉發超過六個 port,或者手機與筆電同時需要預覽畫面時,請改用 在 VPS 上架設 self-hosted WireGuard VPN:開發伺服器綁定於私有介面,並由 ufw 拒絕所有來自公用介面的連線。只有停止在防火牆上開洞,防火牆才能發揮作用。
Claude Code 並非唯一選擇:在 VPS 上執行 coding AI agent 也包含 Aider 與 Goose。
FAQ
當 SSH 連線斷開時,Claude Code 會繼續執行嗎?
只有在 tmux 中啟動時才會繼續執行。直接從 SSH shell 啟動的程序是該 shell 的子程序,當連線中斷時,程序會隨 pty 一起結束。若在 tmux 內執行,shell 屬於已分離 (detached) 的 tmux server,因此 agent 會繼續執行任務,且 tmux attach 會讓你回到相同的捲動紀錄。請在每次登入後將 tmux new -A -s <project> 作為第一個指令,即可解決此問題。
我應該使用 sudo npm install -g 來安裝 CLI 嗎?
不應該。使用 root 權限的 global prefix 會導致後續安裝出現 EACCES 錯誤,並在 npm cache 中產生 root 權限檔案。請將 npm 的 prefix 設定為 ~/.npm-global(或使用 nvm 等版本管理工具),以非特權用戶 agent 身份進行安裝,並從 ~/.bashrc 將 ~/.npm-global/bin 匯出至 PATH,需位於互動式保護機制之上。若您先前已執行過 sudo npm,請使用 sudo chown -R $(id -u):$(id -g) ~/.npm 修復 cache。
在執行中的機器上使用 ssh -A agent forwarding 安全嗎?
這會授予比工作需求更多的權限。Forwarding 會將您本地 SSH agent 的 socket 暴露給該用戶下的所有程序,因此只要您保持連線,機器上的任何程序都能請求您的金鑰,為其可觸及的任何主機進行簽章。建議在伺服器上生成 ed25519 金鑰,並將其註冊為單一儲存庫的 deploy key,僅在 agent 確實需要 push 時才授予寫入權限。
為什麼我的 build 只會印出 Killed?
若只有單一字串且沒有 stack trace,那是 kernel OOM killer。請使用 sudo dmesg -T | grep -i -E 'out of memory|killed process' 確認;若從 Node 執行,可能會看到 JavaScript heap out of memory。請依序嘗試以下修復方法:新增 swapfile、限制測試與編譯器的並行度 (parallelism)、提高 NODE_OPTIONS=--max-old-space-size=...,最後再升級 VPS 規格。請注意,OOM killer 可能會選中 tmux server 而非 build 程序,導致整個 session 一併結束。
該使用 tmux 還是 systemd service?
tmux 適用於您可以連接、監控並輸入指令的互動式 session,這正是 agent session 的特性。若工作是定時執行且無需人工監控,則應使用 systemd unit 與 timer,這能直接獲得日誌記錄、重啟策略與開機自動啟動功能。如果您打算使用 tmux 來執行類似 cron 的工作,該工作應該被設定為一個 service。