SSD Nodes Learn Hosting plans →
Mga Gabay Matt ConnorNi Matt Connor · Na-update 2026-08-30

Claude Code sa VPS gamit ang tmux

Patakbuhin ang Claude Code sa always-on Linux VPS at tmux para hindi mamatay ang agent kapag naputol ang SSH. Alamin ang setup at failure modes.

Ang problema ay ang takip ng laptop, hindi ang CLI

Maayos na tumatakbo ang Claude Code sa laptop mo hanggang sa isara mo ito: nawawala ang SSH session, nakakatanggap ang shell ng SIGHUP, at kasabay nito ay namamatay ang agent tatlong minuto pa lang sa test run. Patakbuhin ang CLI sa isang machine na hindi nagso-sleep, sa loob ng terminal multiplexer na ang mga proseso ay hindi mga child process ng SSH session mo. Iyon lang ang buong solusyon. Ang mahalagang bahagi ay ang tmux, hindi ang installation.

Tungkol ang pahinang ito sa pag-operate ng isang box na iniiwan mong nagpapatakbo ng mga agent. Kung wala kang Linux server na maaari mong iwang naka-on, hindi ito naaangkop sa iyo. Iyon ang isang tapat na prerequisite.

Ang aktuwal na ginagawa ng tmux

Kapag nag-SSH ka, ang sshd ay nagfa-fork ng shell at binibigyan ito ng pseudo-terminal; child nito ang lahat ng sinisimulan mo mula sa shell na iyon. Kapag naputol ang connection, inaalis ng kernel ang pty, nakakatanggap ang shell ng SIGHUP, at ipinapasa naman nito ang hangup sa mga child process nito. Mamamatay ang mga long-running process na nasa foreground.

Binabaligtad ng tmux ang ownership. Ang command na tmux na tina-type mo ay isang thin client na nakikipag-ugnayan sa pamamagitan ng unix socket sa tmux server na hiwalay na tumatakbo mula sa iyong terminal. Ang mga shell sa loob ng session ay child ng server na iyon, hindi ng sshd. Kapag pinutol ang SSH connection, mawawala ang client habang patuloy na tumatakbo ang server, ang session, at ang agent na may isinasagawang task. Kumonekta muli, tmux attach, at babalik ka sa parehong shell na may parehong scrollback. Nakakaligtas din ang nohup sa hangup, pero wala itong paraan para makabalik ka rito; hindi mo maaaring i-re-attach ang isang naka-background na TUI. Interactive ang Claude Code; tmux, o screen, ang tamang tool.

Pag-size ng machine

Ang CLI ay isang Node process; hindi ito ang kumukonsumo sa buong machine. Ang kumokonsumo sa machine ay ang anumang pinapatakbo ng agent para sa iyo: isang build, buong test suite, tsc, language server, o database sa Docker. I-size ang machine batay sa toolchain, hindi sa CLI. Magdagdag ng swap kahit hindi mo planong gamitin ito kailanman. Napapalitan nito ang biglaang OOM kill ng mabagal na 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

Bantayan din ang disk. Mabilis dumami ang repos, node_modules, at Docker images. Kung umaabot ang toolchain lampas sa containers at gumagamit ng mga buong virtual machine, KVM guest, o lokal na Kubernetes node, tiyaking naka-expose sa plan ang CPU virtualisation extensions bago ka mag-commit. Ang pagpapatakbo ng nested virtualization sa isang VPS ay kailangang i-enable ng provider; hindi mo ito maaaring i-on mula sa loob ng guest.

Isang non-root user muna

Gumawa ng dedicated user na may sarili nitong home directory, at ilagay dito ang iyong 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

Subukan ang pag-login mula sa pangalawang terminal bago magpatuloy, habang naroon pa ang password authentication bilang fallback. Kung lumabas ang Hindi pinayagan ang access (publickey), karaniwang nasa ownership o mode ng .ssh directory ang problema, hindi sa key mismo.

Sinasadya na hindi kabilang ang agent sa sudo group. Kung kailangan ng system package, i-install ito. Inaalis ng pasyang ito ang karamihan sa mga paraan kung paano maaaring masira ng isang hindi sinasadyang shell command ang host.

SSH hygiene para sa machine na patuloy mong pinapatakbo

Ang password authentication sa machine na buong araw ay nasa public internet at may hawak na agent at source code mo ay panganib na hindi sulit panatilihin. I-disable ito. Sa Ubuntu 24.04 at Debian 13, kasama sa /etc/ssh/sshd_config ang /etc/ssh/sshd_config.d/*.conf, kaya maglagay ng file sa halip na i-edit ang pangunahing configuration:

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

I-validate at i-reload ang configuration. Panatilihing bukas ang kasalukuyan mong session habang sumusubok ka ng bagong session mula sa pangalawang terminal:

sudo sshd -t && sudo systemctl restart ssh

May isang mahalagang detalye sa Ubuntu 24.04: socket-activated ang sshd. Naa-apply ang authentication settings sa systemctl restart ssh, pero kailangan din ng systemctl daemon-reload at pag-restart ng ssh.socket kapag binago ang listening Port.

Kasunod nito ang firewall. Payagan ang SSH bago mo ito i-enable, kung hindi ay mai-lock out ka:

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

I-install ang fail2ban nang malinaw sa iyo kung ano ang pakinabang nito: kapag naka-disable na ang password authentication, hindi na magtatagumpay ang brute-force attack; inaalis nito sa iyong journal ang mga nabigong pagtatangka.

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

Panghuli, awtomatikong mag-install ng patches gamit ang sudo apt install unattended-upgrades at sudo dpkg-reconfigure -plow unattended-upgrades. Tandaan ang epekto nito sa tmux: kapag i-enable mo ang Unattended-Upgrade::Automatic-Reboot at nagkaroon ng kernel update, magre-reboot ang machine at matatapos ang lahat ng session kasama nito. Panatilihin itong naka-disable at ikaw ang mag-reboot sa sarili mong iskedyul, kapag walang prosesong kasalukuyang tumatakbo. Ganito rin ang pag-iingat sa release upgrade: ang paglipat ng machine mula Ubuntu 24.04 patungong 26.04 ay nagre-restart ng sshd at ng kernel, kaya gawin ito sa oras na walang tmux session na may mahalagang gawain.

Mag-install ng Node.js at Claude Code sa Ubuntu

Ang Claude Code ay isang Node CLI, kaya kailangan mo ng kasalukuyang Node. Madalas luma ang distro package; karaniwang paraan sa Ubuntu at Debian ang NodeSource, at may kasama itong 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

Dito karaniwang nagkakamali ang mga tao: i-install ang CLI bilang user na agent, at huwag kailanman gumamit ng sudo npm -g. Ang global prefix na pagmamay-ari ng root ay nagdudulot ng mga permission error sa susunod at nag-iiwan ng mga file na pagmamay-ari ng root sa npm cache. Ituro muna ang prefix ng npm sa home 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

Ilagay ang export sa ~/.bashrc, hindi sa ~/.profile, at dapat itong nasa itaas ng guard na “If not running interactively, don't do anything” malapit sa itaas ng file: maaaring magsimula ang tmux ng mga non-login shell, na nagbabasa ng ~/.bashrc at nilalaktawan ang ~/.profile; tumatakbo lamang ang ~/.profile para sa mga login shell. Pareho rin ang resulta kapag gumamit ng per-user Node sa pamamagitan ng version manager gaya ng nvm; ang mahalaga ay hindi kailanman kailangan ng npm install -g ang sudo. Maayos pa ring gumagana ang npm, o gamitin ang native install script ng Anthropic, na siyang kasalukuyang dokumentadong default. Suriin ang install docs ng Anthropic bago mag-paste, dahil nagbabago ang mga paraan ng pag-install.

Patakbuhin ang claude sa loob ng isang repo upang simulan ito. Sa unang pagtakbo, gagabayan ka nito sa authentication; walang browser ang headless box, kaya magbibigay ang flow ng URL na bubuksan sa sarili mong machine at code na ibabalik sa terminal. (Ang API key sa environment ang isa pang paraan.) Sa alinmang paraan, nasa server na ngayon ang credential na iyon, kaya susunod na natin ang bahaging madalas nilalaktawan ng mga tao.

Ang usapin ng lawak ng epekto

Ang agent na may shell access ay may shell. Mababasa nito ang anumang nababasa ng user kung saan ito tumatakbo, at makakapag-push ito sa anumang mapagpa-push-an ng user na iyon. Hindi ito puna sa tool. Ito ang mismong kahulugan nito, kaya mas mahalaga ang account kung saan ito tumatakbo kaysa sa anumang indibidwal na setting.

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

May flag ang Claude Code na ganap na lumalampas sa mga permission prompt nito. Sa laptop o throwaway project, nasa iyo ang desisyon. Sa server na may hawak na mga token, inaalis nito ang huling hadlang sa pagitan ng maling pagkakaintindi sa isang instruction at ng git push --force. Hindi rin all-or-nothing ang mga prompt na nilalampasan nito. Dahil paparating na default ang auto mode, mahalagang malaman kung aling permission mode ang dapat i-pin sa server na hindi mo mino-monitor. Saklaw ng ligtas na pagpapatakbo ng Claude Code sa server kung ano talaga ang binabago ng flag at kung paano iko-contain ang agent na tumatakbo gamit nito, mula sa built-in sandbox hanggang sa disposable VPS.

Deploy key kumpara sa SSH agent forwarding

Nakakatuksong ssh -A para magamit ng git ang key sa laptop mo. Unawain kung ano ang ibinibigay nito: inilalantad ng agent forwarding ang socket ng local SSH agent mo sa mga process na tumatakbo bilang user na iyon sa box. Anumang tumatakbo bilang agent, kabilang ang agent, ay maaaring humiling sa key mo na lumagda para sa anumang host na maaabot nito, hangga't nananatiling nakakonekta ka. Higit ito sa “payagan ang git na mag-pull mula sa repository na ito”.

Sa halip, bumuo ng key sa server, i-register ito bilang per-repository deploy key (write access lamang kung kailangang mag-push ng agent), at magtakda ng git identity upang 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 workflow sa tmux

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 sapat para sa pang-araw-araw na paggamit:

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

Ang tmux new -A -s claude ang dapat mong kabisaduhin. Kumokonekta ito sa session kung umiiral na ito at ginagawa ito kung wala pa. Dahil dito, isang command lang ang kailangan para sa pagsisimula ng araw at pagpapatuloy pagkatapos maputol ang koneksyon. Gumawa ng alias para rito. Sa loob ng session, binubuksan ng Ctrl-b c ang isang window, at nagpapalipat-lipat sa mga ito ang Ctrl-b n at Ctrl-b p. Ginagamit ang Ctrl-b [ para pumasok sa copy mode at mag-scroll pabalik (q para lumabas).

Mahalagang malaman ang tungkol sa mga session na hindi mo kailanman winawakasan: muling ipinapadala ng agent ang buong conversation sa bawat turn. Basahin ang kung saan napupunta ang tokens ng isang matagal na Claude Code session bago ka mag-iwan ng session na tumatakbo nang isang linggo.

Mga failure mode

“Nawala ang session ko.” Ipinapakita ng tmux ls ang no server running on /tmp/tmux-1000/default. Halos palagi itong nangangahulugang hindi kailanman nasa loob ng tmux ang process. Nag-SSH ka, direktang nagpatakbo ng claude, at pinatay ito ng pagkakadiskonekta. Walang mare-recover. Ang ugaling pumipigil dito: ang tmux new -A -s <project> ang dapat na unang command pagkatapos ng bawat login.

Lumiit ang pane at naging napakaliit na box. Itinatakda ng tmux ang laki ng session ayon sa pinakamaliit na nakakabit na client. Kaya maaaring paliitin ng stale client na nakakabit pa mula sa ibang machine ang display. Piliting i-disconnect ang iba habang kumokonekta ka: tmux attach -d -t claude.

Nagpi-print ang build ng Killed. Isang salita lang ito at walang stack trace. Kumpirmahin gamit ang sudo dmesg -T | grep -i -E 'out of memory|killed process'. Pinili ng kernel OOM killer ang pinakamalaking process. Mula sa Node, maaari mong makita ang FATAL ERROR: Ineffective mark-compacts near heap limit Allocation failed - JavaScript heap out of memory. Mga paraan ng pag-aayos, ayon sa pagkakasunod-sunod: magdagdag ng swap (sa itaas), limitahan ang parallelism ng test at compiler, taasan ang heap ng Node gamit ang NODE_OPTIONS=--max-old-space-size=..., o gumamit ng mas malaking VPS. Maaari ring piliin ng OOM killer ang tmux server sa halip na ang build, kaya mawawala rin ang session mo. Kung tumatakbo ang systemd-oomd, maaari nitong patayin ang buong user slice at magdulot ng parehong resulta.

npm error code EACCES / permission denied, mkdir '/usr/lib/node_modules/...'. Global install ito sa prefix na pagmamay-ari ng root. Gamitin ang ~/.npm-global prefix sa itaas. Kung dati mo nang pinatakbo ang sudo npm, maaari mo ring makita ang Your cache folder contains root-owned files. Ayusin ito gamit ang sudo chown -R $(id -u):$(id -g) ~/.npm.

claude: command not found, pero paminsan-minsan lang. Nasa ~/.bashrc ang export ng PATH, sa ibaba ng guard na “If not running interactively, don't do anything,” kaya nilalaktawan ito ng mga non-interactive shell. 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 shell. Binabasa ng mga ito ang ~/.bashrc at hindi kailanman binabasa ang ~/.profile.

Magulong mga kulay pagkatapos kumonekta. May mismatch sa TERM. Ang linyang default-terminal sa itaas ang solusyon.

Nawawala ang mga session pagkatapos ng reboot. Hindi ito bug. Ang tmux server ay isang process, at tinatapos ito ng reboot. Suriin ang uptime.

Mga problemang lumalabas habang lumalaki ito

Mas maraming proyekto. Gumamit ng isang tmux session para sa bawat repo at pangalanan ito ayon sa repo; ang tmux ls ang magsisilbing dashboard mo. Kapag hindi mo sinunod ang maayos na pagpapangalan, magkakaroon ka ng mga session na 0, 1, at 2. Kapag sabay-sabay nang tumatakbo ang ilan, hindi nila kailangang gumana nang hiwa-hiwalay, dahil maaaring magpadala ng mensahe ang isang session sa isa pa sa parehong box. Kapaki-pakinabang ito kapag gusto ng agent na nagsasagawa ng matagal na refactor na magpatakbo ang isa pang agent ng mga test. Ganoon din lumalaki ang bilang ng mga port. Kapag anim na repo ang nangangailangan ng :3000, oras na para itigil ang manu-manong pagtatalaga ng mga port at hayaang mag-route ng Traefik reverse proxy ng maraming app sa Docker Compose batay sa hostname.

Mas maraming tao. Per-user ang tmux socket. Kaya ang dalawang developer sa parehong box ay may kanya-kanyang tmux server at hindi nila nakikita ang mga session ng isa't isa. Kapag nagbahagi kayo ng isang session sa pamamagitan ng shared socket, sabay-sabay kayong nagta-type sa parehong shell gamit ang parehong Unix user. Kasama rito ang mga epekto sa audit at permissions. Ang magkahiwalay na user ang simple at tamang sagot.

Mga gawaing walang nagbabantay. Para ang tmux sa mga interactive session na ina-attach mo. Ang mga job na naka-schedule at walang nagbabantay ay dapat nasa systemd unit at timer. Awtomatiko nilang nakukuha ang logging, restart policy, at pagpapatuloy matapos ang boot. Kapag ginagamit mo ang tmux para magpatakbo ng job na karaniwang ginagawa ng cron, senyales ito na dapat maging service ang job.

Isa pang paalala: i-bind ang mga dev server na sinisimulan ng agent sa 127.0.0.1, hindi sa 0.0.0.0, at i-access ang mga ito sa pamamagitan ng SSH tunnel (ssh -L 3000:127.0.0.1:3000 agent@your-server) sa halip na magbukas ng mga port sa ufw. Kapag nagfo-forward ka na ng anim o higit pang port, o parehong gustong gumamit ng iisang preview ang phone at laptop, maglagay na lang ng self-hosted WireGuard VPN sa VPS sa unahan ng mga ito. Mag-bind ang mga dev server sa private interface, at patuloy na i-deny ng ufw ang lahat ng koneksyon mula sa public interface. Makakatulong lamang ang firewall kung hindi ka patuloy na nagbubukas ng mga butas dito.

Hindi lamang si Claude Code ang pagpipilian: sa pagpapatakbo ng coding AI agent sa VPS, kasama ring sinusuri ang Aider at Goose.

FAQ

Nagpapatuloy ba ang Claude Code kapag naputol ang aking SSH connection?

Oo, kung sinimulan mo ito sa loob ng tmux. Ang prosesong direktang inilunsad mula sa SSH shell ay child ng shell na iyon at namamatay kasama ng pty kapag naputol ang connection. Sa loob ng tmux, kabilang ang shell sa detached na tmux server, kaya nagpapatuloy ang agent sa kasalukuyang task at ibinabalik ka ng tmux attach sa parehong scrollback. Gawing tmux new -A -s <project> ang unang command pagkatapos ng bawat login upang mawala ang problemang ito.

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

Hindi. Kapag root ang may-ari ng global prefix, makakatanggap ka ng mga error na EACCES sa mga susunod na install at magkakaroon ng mga file sa npm cache na root ang may-ari. Itakda ang npm prefix sa ~/.npm-global (o gumamit ng version manager gaya ng nvm), mag-install bilang unprivileged na user na agent, at i-export ang ~/.npm-global/bin papunta sa PATH mula sa ~/.bashrc, bago ang interactive guard. Kung naisagawa mo na nang isang beses ang sudo npm, ayusin ang cache gamit ang sudo chown -R $(id -u):$(id -g) ~/.npm.

Ligtas ba ang ssh -A agent forwarding sa isang box na nagpapatakbo ng agent?

Nagbibigay ito ng higit na access kaysa sa kailangan ng job. Inilalantad ng forwarding ang socket ng iyong lokal na SSH agent sa bawat prosesong tumatakbo bilang user na iyon. Dahil dito, maaaring hilingin ng anumang proseso sa box sa iyong key na lumagda para sa anumang host na maaabot nito habang nakakonekta ka. Bumuo ng ed25519 key sa server at i-register ito bilang deploy key para sa isang repository. Bigyan lamang ito ng write access kung kailangang aktuwal na mag-push ng agent.

Bakit Killed lang ang ipinapakita ng build ko?

Ang isang salita na walang stack trace ay karaniwang indikasyon ng kernel OOM killer. Kumpirmahin ito gamit ang sudo dmesg -T | grep -i -E 'out of memory|killed process'; mula sa Node, maaari mong makita sa halip ang JavaScript heap out of memory. Isagawa nang sunod-sunod ang mga pag-aayos: magdagdag ng swapfile, limitahan ang parallelism ng tests at compiler, itaas ang NODE_OPTIONS=--max-old-space-size=..., at pagkatapos ay mag-upgrade sa mas malaking VPS. Mag-ingat dahil maaaring piliin ng OOM killer ang tmux server sa halip na ang build, kaya mawawala ang buong session mo.

tmux o systemd service?

Angkop ang tmux sa mga interactive session na kinokonektahan, mino-monitor, at ginagamitang mag-type. Ito mismo ang katangian ng agent session. Ang mga work na tumatakbo ayon sa schedule nang walang nagbabantay ay dapat ilagay sa systemd unit at timer. May logging, restart policy, at pagpapatuloy pagkatapos ng boot ang mga ito bilang bahagi ng configuration. Kung ginagamit mo ang tmux upang patakbuhin ang isang job na parang cron, service ang kailangan ng job na iyon.