SSD Nodes Learn
Mga Gabay Matt ConnorNi Matt Connor · Na-update 2026-07-24

Paano patakbuhin ang Claude Code sa VPS

Iwasan ang SIGHUP error at pagkaputol ng SSH session sa pamamagitan ng paggamit ng tmux sa iyong Linux VPS para hindi mamatay ang Claude Code agent.

Ang problema ay ang laptop lid, hindi ang CLI

Gumagana nang maayos ang Claude Code sa iyong laptop hanggang sa isara mo ito: mapuputol ang SSH session, makakatanggap ang shell ng SIGHUP, at mamamatay ang agent pagkalipas ng tatlong minuto mula nang magsimula ang test run. Patakbuhin ang CLI sa isang machine na hindi nag-sleep, sa loob ng isang terminal multiplexer kung saan ang mga proseso ay hindi child processes ng iyong SSH session. Iyan ang buong trick — at tmux, hindi ang installation, ang mahalagang bahagi.

Ang page na ito ay tungkol sa pagpapatakbo ng isang machine kung saan nag-iiwan ka ng mga running agents. Kung wala kang Linux server na maaaring iwanang naka-on, hindi ito applicable. Iyan ang tanging prerequisite.

Ang ginagawa talaga ng tmux

Kapag nag-SSH ka, magfo-fork ang sshd ng isang shell at bibigyan ito ng pseudo-terminal; lahat ng sisimulan mo mula sa shell na iyon ay magiging child process nito. Kapag naputol ang connection, tatanggalin ng kernel ang pty, makakatanggap ang shell ng SIGHUP, at ititigil din nito ang lahat ng mga child process nito. Mamamatay ang mga long-running foreground process.

Binaligtad ng tmux ang ownership. Ang tmux command na itina-type mo ay isang thin client na nakikipag-usap via unix socket sa isang tmux server na tumatakbo nang detached mula sa iyong terminal. Ang mga shell sa loob ng isang session ay mga child ng server na iyon, hindi ng sshd. Kapag pinutol ang SSH connection, mawawala ang client habang ang server, ang session, at ang agent ay patuloy na tumatakbo. Mag-reconnect ka, gamitin ang tmux attach, at babalik ka sa parehong shell na may parehong scrollback. Ang nohup ay survives din sa hangup, pero wala itong paraan para makabalik ka — hindi ka maaaring mag-re-attach sa isang backgrounded TUI. Interactive ang Claude Code; ang tmux (o screen) ang tamang tool.

Pag-size ng box

Ang CLI ay isang Node process; hindi ito ang kumakain ng resources sa machine. Ang kumakain ng resources ay ang anumang agent na tumatakbo para sa iyo: isang build, isang full test suite, tsc, isang language server, o isang database sa Docker. I-size ang machine para sa toolchain, hindi para sa CLI. Magdagdag ng swap kahit hindi mo ito balak gamitin — ginagawa nitong mabagal na build ang isang hard OOM kill:

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

Bantayan din ang disk: mabilis maipon ang mga repos, node_modules, at mga Docker image. At kung ang toolchain ay lumalampas sa mga container patungo sa mga full virtual machine — gaya ng KVM guest o isang local Kubernetes node — i-check kung ang plan ay may CPU virtualisation extensions bago mag-commit. Ang running nested virtualization on a VPS ay isang feature na dapat i-enable ng provider sa halip na i-switch on mula sa loob ng guest.

Gumamit muna ng non-root user

Gumawa ng dedicated user na may sariling home directory, at ilagay ang iyong public key sa tamang lokasyon:

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

Sadyang hindi kasama ang agent sa sudo group. Kung kailangan ng system package, i-install ito nang manu-mano. Ang hakbang na ito ang pumipigil sa karamihan ng mga paraan kung saan maaaring masira ng maling shell command ang host.

SSH hygiene para sa mga server na laging naka-on

Ang password auth sa isang machine na nakabukas sa public internet, na may agent at source code, ay isang malaking risk. I-off ito. Sa Ubuntu 24.04 at Debian 13, kasama sa /etc/ssh/sshd_config ang /etc/ssh/sshd_config.d/*.conf, kaya mas mabuting gumamit ng bagong file kaysa i-edit ang main config:

# /etc/ssh/sshd_config.d/10-hardening.conf
PasswordAuthentication no
KbdInteractiveAuthentication no
PermitRootLogin no

I-validate at i-reload — panatilihing bukas ang kasalukuyang session habang sinusubukan ang bagong session mula sa pangalawang terminal:

sudo sshd -t && sudo systemctl restart ssh

Paalala para sa Ubuntu 24.04: socket-activated ang sshd. Ang auth settings ay gumagana sa systemctl restart ssh, pero ang pagbabago sa listening Port ay nangangailangan din ng systemctl daemon-reload at restart ng ssh.socket.

Pagkatapos ay ang firewall. I-allow muna ang SSH bago ito i-enable, kung ayaw mong ma-lock out:

sudo ufw allow OpenSSH
sudo ufw default deny incoming
sudo ufw default allow outgoing
sudo ufw enable

I-install ang fail2ban nang may malinaw na pag-unawa sa gamit nito: kapag naka-off na ang password auth, hindi na magtatagumpay ang brute force — pinipigilan nito ang mga failed attempts sa iyong journal.

# /etc/fail2ban/jail.local
[sshd]
enabled = true
backend = systemd
maxretry = 5
bantime = 1h

Panghuli, mag-patch nang automatic gamit ang sudo apt install unattended-upgrades at sudo dpkg-reconfigure -plow unattended-upgrades. Tandaan ang epekto nito sa tmux: kapag naka-on ang Unattended-Upgrade::Automatic-Reboot at nagkaroon ng kernel update, magre-reboot ang box at mawawala ang lahat ng session. I-off ito at mag-reboot na lang sa sarili mong schedule kapag walang tumatakbong proseso.

I-install ang Node.js at Claude Code sa Ubuntu

Ang Claude Code ay isang Node CLI, kaya kailangan mo ng current na Node version. Madalas ay outdated ang distro package; ang NodeSource ang karaniwang paraan sa Ubuntu at Debian, at nagbibigay ito ng signed repo (walang apt-key — wala na ang tool na iyon):

curl -fsSL https://deb.nodesource.com/setup_24.x | sudo -E bash -
sudo apt install -y nodejs
node --version

Narito ang bahaging madalas magkamali ang mga user: i-install ang CLI gamit ang iyong agent user, huwag kailanman gamitin ang sudo npm -g. Ang global prefix na pagmamay-ari ng root ay magdudulot ng permission errors sa hinaharap at mag-iiwan ng mga file na root-owned sa npm cache. Ituro muna ang npm prefix sa home directory ng user:

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 --version

Ang export ay dapat ilagay sa ~/.bashrc, hindi sa ~/.profile, at dapat itong ilagay bago ang "If not running interactively, don't do anything" na guard sa itaas ng file: ang tmux ay maaaring mag-start ng mga non-login shell, na nagbabasa ng ~/.bashrc at lumalagpasan ang ~/.profile — ang ~/.profile ay tumatakbo lamang para sa mga login shell. Ang paggamit ng per-user Node via version manager gaya ng nvm ay may parehong resulta; ang layunin ay para hindi na kailanganin ng npm install -g ang sudo. Gumagana pa rin nang maayos ang npm, o maaari ring gamitin ang native install script ng Anthropic, na siyang kasalukuyang documented default. Suriin muna ang install docs ng Anthropic bago mag-paste — nagbabago ang mga install method.

I-run ang claude sa loob ng isang repo para simulan ito. Ang unang run ay gagabay sa iyo sa authentication; ang isang headless box ay walang browser, kaya bibigyan ka ng URL na dapat buksan sa iyong sariling machine at isang code na dapat ibalik sa terminal. (Ang API key sa environment ay isa pang paraan.) Sa alinmang paraan, ang credential na iyon ay nasa server na — na magdadala sa atin sa bahaging madalas laktawan ng mga user.

Ang usaping blast radius

Ang agent na may shell access ay isang shell. Kaya nitong basahin ang lahat ng nababasa ng user na nagpapatakbo nito, at kaya nitong mag-push sa lahat ng pwedeng i-push ng user na iyon. Hindi ito kritisismo sa tool; ito ang depinisyon nito — at ito ang dahilan kung bakit mas mahalaga ang account na pinapatakbo nito kaysa sa anumang indibidwal na setting.

  • Dedicated, unprivileged user. Walang sudo group, at walang home directory na shared sa sarili mong account.
  • Walang production credentials sa box. Walang ~/.aws/credentials na may hawak na prod keys, walang .env na kinopya mula sa production, at walang database password na may write access sa anumang kritikal. Bigyan ang agent ng staging o read-only credential.
  • Scoped tokens. Isang fine-grained GitHub token na limitado sa isang repository; o deploy key kung sapat na ang read access.

May flag ang Claude Code na tuluyang nag-i-skip sa mga permission prompt nito. Sa laptop o sa isang throwaway project, desisyon mo iyan. Ngunit sa isang server na may hawak na mga token, tinatanggal nito ang huling proteksyon sa pagitan ng maling instruction at ng git push --force. Ang tunay na epekto ng flag na ito, at kung paano limitahan ang isang agent na tumatakbo gamit ito — mula sa built-in sandbox hanggang sa isang disposable VPS — ay tatalakayin sa pagpapatakbo ng Claude Code nang ligtas sa isang server.

Deploy key vs SSH agent forwarding

Maka-akit ang mag-ssh -A para magamit ng git ang key sa iyong laptop. Unawain ang ibinibigay nito: ang agent forwarding ay naglalantad sa socket ng iyong local SSH agent sa mga prosesong tumatakbo bilang user na iyon sa box. Anumang tumatakbo bilang agent — kasama ang agent — ay maaaring humiling sa iyong key na mag-sign para sa kahit anong host na maaabot nito, basta't nananatili kang connected. Higit pa ito sa "hayaang i-pull ng git ang isang repo na ito".

Sa halip, mag-generate ng key sa server, i-register ito bilang per-repository deploy key (write access lang kung kailangang mag-push ng agent), at mag-set ng git identity para makilala ang mga commit mula sa box:

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"

Ang tmux workflow

I-install ito (sudo apt install tmux), pagkatapos ay gumawa ng minimal na ~/.tmux.conf:

set -g mouse on
set -g history-limit 50000
set -g default-terminal "tmux-256color"

Apat na command ang kailangan para sa pang-araw-araw na gamit:

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

Dapat kabisaduhin ang tmux new -A -s claude — mag-a-attach ito kung existing na ang session, at gagawa naman kung wala pa. Dahil dito, isang command lang ang kailangan para sa pagsisimula ng trabaho o pagbabalik sa session na naputol. Gawin itong alias. Sa loob ng session, nagbubukas ang Ctrl-b c ng window, ang Ctrl-b n at Ctrl-b p ay para sa pag-cycle sa mga ito, at ang Ctrl-b [ ay para sa copy mode para makapag-scroll pabalik (q ang pang-exit).

Isang mahalagang paalala tungkol sa mga session na hindi pinapatay: muling ipinapadala ng agent ang buong conversation sa bawat turn. Basahin ang kung saan ginagamit ng isang long-lived Claude Code session ang tokens nito bago mag-iwan ng session na tumatakbo nang isang linggo.

Failure modes

"My session is gone." Nag-print ang tmux ls ng no server running on /tmp/tmux-1000/default. Halos laging ibig sabihin nito ay hindi tumakbo ang process sa loob ng tmux — nag-SSH ka, tinakbo ang claude nang direkta, at pinatay ito ng disconnect. Walang maibabalik. Ang paraan para maiwasan ito: gawing unang command ang tmux new -A -s <project> pagkatapos ng bawat login.

Lumaliliit ang pane sa isang maliit na box. Ina-adjust ng tmux ang laki ng session base sa pinakamaliit na attached client, kaya ang isang stale client na naka-attach mula sa ibang machine ay nagpapakipot sa display. I-disconnect ang iba habang nag-a-attach ka: gamitin ang tmux attach -d -t claude.

Nag-print ang build ng Killed. Isang salita lang, walang stack trace. I-verify gamit ang sudo dmesg -T | grep -i -E 'out of memory|killed process' — pinili ng kernel OOM killer ang pinakamalaking process. Sa Node, maaaring FATAL ERROR: Ineffective mark-compacts near heap limit Allocation failed - JavaScript heap out of memory ang makita sa halip. Mga solusyon, ayon sa pagkakasunod-sunod: magdagdag ng swap (tingnan sa itaas), limitahan ang test at compiler parallelism, taasan ang heap ng Node gamit ang NODE_OPTIONS=--max-old-space-size=..., o i-upgrade ang laki ng VPS. Maaari ring piliin ng OOM killer ang tmux server sa halip na ang build, kaya mawawala ang iyong session; kung tumatakbo ang systemd-oomd, maaari nitong patayin ang buong user slice na may parehong epekto.

npm error code EACCES / permission denied, mkdir '/usr/lib/node_modules/...'. Isang global install sa isang root-owned prefix. Gamitin ang ~/.npm-global prefix na nasa itaas. Kung tumakbo ka na ng sudo npm dati, maaaring makakita ka rin ng Your cache folder contains root-owned files — ayusin gamit ang sudo chown -R $(id -u):$(id -g) ~/.npm.

claude: command not found — pero paminsan-minsan lang. Ang iyong PATH export ay nasa loob ng ~/.bashrc sa ilalim ng "If not running interactively, don't do anything" na guard, kaya hindi ito binabasa ng mga non-interactive shells. Ilipat ang export sa itaas ng guard na iyon at panatilihin ito sa ~/.bashrc, hindi sa ~/.profile: maaaring magsimula ang tmux ng mga non-login shells, na nagbabasa ng ~/.bashrc at hindi na tinatamaan ang ~/.profile.

Magulong kulay pagkatapos mag-attach. May TERM mismatch — ang default-terminal line sa itaas ang solusyon.

Nawawala ang mga session pagkatapos ng reboot. Hindi ito bug: ang tmux server ay isang process, at ang reboot ay nagtatapos dito. I-check ang uptime.

Ano ang nasisira habang lumalaki ang scale

Mas maraming projects. Gumamit ng isang tmux session bawat repo, at pangalanan ito ayon sa repo; tmux ls na ang magsisilbing dashboard mo. Kapag hindi sumunod sa naming discipline, magkakaroon ka ng mga session na 0, 1, at 2. Ganoon din ang pagkalat ng mga ports — kapag anim na repo ang nangangailangan ng :3000, dapat nang itigil ang manual assignment at gamitin ang isang Traefik reverse proxy route multiple apps under Docker Compose para sa dispatching gamit ang hostname.

Mas maraming tao. Ang mga tmux socket ay per-user, kaya ang dalawang developer sa iisang box ay may kani-kaniyang tmux server at hindi makikita ang sessions ng isa't isa. Ang paggamit ng iisang session sa isang shared socket ay nangangahulugang lahat ay nagta-type sa iisang shell bilang iisang Unix user, na may kaakibat na audit at permission consequences. Ang paggamit ng magkakaibang users ang boring ngunit tamang solusyon.

Unattended work. Ang tmux ay para sa mga interactive sessions na ina-attach mo. Ang mga job na tumatakbo sa schedule nang walang nakabantay ay dapat nasa loob ng isang systemd unit at timer, kung saan may libre na silang logging, restart policy, at boot survival. Ang paggamit ng tmux para sa isang cron-shaped job ay senyales na ang job na iyon ay dapat maging isang service.

Isang huling paalala: i-bind ang mga dev server sa 127.0.0.1, hindi sa 0.0.0.0, at i-access ang mga ito via SSH tunnel (ssh -L 3000:127.0.0.1:3000 agent@your-server) sa halip na magbukas ng mga ports sa ufw. Kapag nagfo-forward ka na ng kalahating dosenang ports, o kung ang isang phone at laptop ay parehong gustong mag-access sa iisang preview, maglagay na ng self-hosted WireGuard VPN on the VPS sa harap nila: ang mga dev server ay magba-bind sa isang private interface, at itutuloy ng ufw ang pag-deny sa lahat mula sa public interface. Ang firewall ay makakatulong lamang kung ititigil mo ang pagbubutas dito.

Hindi lang Claude Code ang pagpipilian: ang running a coding AI agent on a VPS ay kinabibilangan din ng Aider at Goose.

FAQ

Patuloy bang tatakbo ang Claude Code kahit maputol ang SSH connection ko?

Tanging kung sinimulan mo ito sa loob ng tmux. Ang process na direktang inilunsad mula sa SSH shell ay child process ng shell na iyon at mamamatay kasama ang pty kapag naputol ang link. Sa loob ng tmux, ang shell ay bahagi ng detached tmux server, kaya patuloy na gagana ang agent sa gitna ng task at ibabalik ka ng tmux attach sa parehong scrollback. Gawing tmux new -A -s <project> ang unang command pagkatapos ng bawat login para maiwasan ang problema.

Dapat ko bang i-install ang CLI gamit ang sudo npm install -g?

Hindi. Ang global prefix na pagmamay-ari ng root ay magdudulot ng EACCES errors sa mga susunod na install at root-owned files sa npm cache. I-set ang npm prefix sa ~/.npm-global (o gumamit ng version manager gaya ng nvm), i-install bilang unprivileged agent user, at i-export ang ~/.npm-global/bin sa PATH mula sa ~/.bashrc, sa itaas ng interactive guard. Kung nagamit mo na ang sudo npm nang isang beses, i-repair ang cache gamit ang sudo chown -R $(id -u):$(id -g) ~/.npm.

Ligtas ba ang ssh -A agent forwarding sa isang box na may tumatakbong agent?

Nagbibigay ito ng higit pa sa kailangan ng trabaho. Ang forwarding ay naglalantad sa socket ng iyong local SSH agent sa bawat process na tumatakbo bilang user na iyon, kaya anumang bagay sa box ay maaaring gumamit ng iyong key para mag-sign para sa anumang host na maaabot nito hangga't naka-attach ka. Mag-generate ng ed25519 key sa server at i-register ito bilang per-repository deploy key, na may write access lamang kung kailangang mag-push ng agent.

Bakit Killed lang ang lumalabas sa build ko?

Ang isang salita na walang stack trace ay ang kernel OOM killer. I-confirm ito gamit ang sudo dmesg -T | grep -i -E 'out of memory|killed process'; mula sa Node, maaaring JavaScript heap out of memory ang makita mo. Gawin ang mga fix ayon sa pagkakasunod-sunod: magdagdag ng swapfile, limitahan ang test at compiler parallelism, itaas ang NODE_OPTIONS=--max-old-space-size=..., at pagkatapos ay i-upgrade ang laki ng VPS. Tandaan na maaaring piliin ng OOM killer ang tmux server sa halip na ang build, na magreresulta sa pagkawala ng iyong buong session.

tmux o systemd service?

Ang tmux ay angkop para sa mga interactive session na iyong ina-attach, pinapanood, at tina-type, na siyang mismong katangian ng isang agent session. Ang trabahong tumatakbo ayon sa schedule nang walang nagbabantay ay dapat nasa loob ng isang systemd unit at timer, kung saan libre na ang logging, restart policy, at boot survival. Kung gagamit ka ng tmux para magpatakbo ng cron-shaped job, ang trabahong iyon ay dapat maging isang service.