Cara guna Claude Code pada VPS dengan tmux
Jalankan Claude Code pada Linux VPS menggunakan tmux supaya sesi ejen tidak terhenti apabila sambungan SSH terputus atau komputer riba ditutup.
Masalahnya adalah penutup komputer riba, bukan CLI
Claude Code berfungsi dengan baik pada komputer riba anda sehinggalah anda menutupnya: sesi SSH terputus, shell menerima SIGHUP, dan ejen akan terhenti selepas tiga minit menjalankan ujian. Jalankan CLI pada mesin yang tidak pernah masuk ke mod tidur, di dalam terminal multiplexer yang prosesnya bukan anak (child process) kepada sesi SSH anda. Itulah cara penyelesaiannya — tmux adalah komponen utama, bukannya proses pemasangan.
Halaman ini adalah mengenai pengendalian pelayan yang membiarkan ejen terus berjalan. Jika anda tidak mempunyai pelayan Linux yang boleh dibiarkan sentiasa hidup, semua maklumat ini tidak terpakai. Itu adalah satu-satunya prasyarat yang wajib.
Apa yang sebenarnya dilakukan oleh tmux
Apabila anda melakukan SSH, sshd melakukan fork pada shell dan memberikan pseudo-terminal; setiap perkara yang anda mulakan daripada shell tersebut adalah anak (child) kepadanya. Jika sambungan terputus, kernel akan memusnahkan pty, shell akan menerima SIGHUP, dan ia akan memutuskan sambungan kepada anak-anaknya. Proses foreground yang berjalan lama akan terhenti.
tmux membalikkan pemilikan tersebut. Perintah tmux yang anda taip adalah klien nipis yang berkomunikasi melalui unix socket kepada tmux server yang berjalan secara terpisah daripada terminal anda. Shell di dalam sesi adalah anak kepada server tersebut, bukan kepada sshd. Jika sambungan SSH terputus, klien akan hilang manakala server, sesi, dan ejen yang sedang menjalankan tugas akan terus berjalan. Sambung semula, tmux attach, dan anda akan kembali ke shell yang sama dengan sejarah skrol yang sama. nohup juga terselamat daripada pemutusan sambungan, tetapi ia tidak memberi jalan untuk masuk semula — anda tidak boleh menyambung semula (re-attach) ke TUI yang sedang berjalan di latar belakang. Claude Code adalah interaktif; tmux (atau screen) adalah alat yang tepat.
Saiz mesin
CLI ialah proses Node; ia bukan faktor utama penggunaan sumber mesin. Penggunaan sumber mesin ditentukan oleh apa sahaja yang dijalankan oleh ejen bagi pihak anda: proses binaan, suite ujian lengkap, tsc, pelayan bahasa, atau pangkalan data dalam Docker. Sediakan saiz yang mencukupi untuk rangkaian alatan (toolchain), bukan untuk CLI sahaja. Tambah swap walaupun anda tidak bercadang untuk menggunakannya — ini menukarkan kegagalan OOM yang mendadak kepada proses binaan yang perlahan:
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/fstabPantau penggunaan cakera juga: repositori, node_modules dan imej Docker akan bertambah dengan cepat. Jika rangkaian alatan anda melangkaui kontena sehingga melibatkan mesin maya sepenuhnya — seperti tetamu KVM atau nod Kubernetes tempatan — pastikan pelan anda menyokong sambungan virtualisasi CPU sebelum anda melanggan, kerana menjalankan virtualisasi bersarang pada VPS adalah ciri yang diaktifkan oleh penyedia perkhidmatan, bukannya sesuatu yang boleh diaktifkan dari dalam tetamu.
Pengguna bukan-root terlebih dahulu
Cipta pengguna khas dengan direktori home sendiri, dan letakkan kunci awam anda di tempat yang betul:
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_keysSecara sengaja, agent tidak berada dalam kumpulan sudo. Jika pakej sistem diperlukan, anda perlu memasangnya. Keputusan tersebut menghalang kebanyakan cara arahan shell yang tersilap boleh merosakkan hos.
Kebersihan SSH untuk pelayan yang sentiasa aktif
Pengesahan kata laluan pada mesin yang terdedah kepada internet awam sepanjang hari, yang menyimpan agent dan kod sumber anda, adalah risiko yang tidak berbaloi. Matikan fungsi tersebut. Pada Ubuntu 24.04 dan Debian 13, /etc/ssh/sshd_config merangkumi /etc/ssh/sshd_config.d/*.conf, jadi cipta fail baharu daripada menyunting konfigurasi utama:
# /etc/ssh/sshd_config.d/10-hardening.conf
PasswordAuthentication no
KbdInteractiveAuthentication no
PermitRootLogin noSahkan dan muat semula — biarkan sesi semasa anda terbuka semasa anda menguji sesi baharu daripada terminal kedua:
sudo sshd -t && sudo systemctl restart sshPerincian pada Ubuntu 24.04: sshd diaktifkan melalui socket. Tetapan pengesahan terpakai pada systemctl restart ssh, tetapi perubahan pada Port yang sedang mendengar juga memerlukan systemctl daemon-reload dan pemulaan semula ssh.socket.
Kemudian tetapkan tembok api. Benarkan SSH sebelum anda mengaktifkannya, atau anda akan terkunci daripada sistem:
sudo ufw allow OpenSSH
sudo ufw default deny incoming
sudo ufw default allow outgoing
sudo ufw enablePasang fail2ban dengan memahami kegunaannya: sebaik sahaja pengesahan kata laluan dimatikan, serangan brute force tidak akan berjaya — ia mengekalkan percubaan yang gagal daripada memenuhi jurnal anda.
# /etc/fail2ban/jail.local
[sshd]
enabled = true
backend = systemd
maxretry = 5
bantime = 1hAkhir sekali, kemas kini secara automatik dengan sudo apt install unattended-upgrades dan sudo dpkg-reconfigure -plow unattended-upgrades. Perhatikan interaksi dengan tmux: jika Unattended-Upgrade::Automatic-Reboot diaktifkan dan kemas kini kernel berlaku, pelayan akan dimulakan semula dan menutup semua sesi. Biarkan ia dimatikan dan mulakan semula mengikut jadual anda sendiri, apabila tiada proses yang sedang berjalan.
Pasang Node.js dan Claude Code pada Ubuntu
Claude Code adalah CLI Node, jadi anda memerlukan Node versi terkini. Pakej distro selalunya ketinggalan; NodeSource adalah cara biasa pada Ubuntu dan Debian, dan ia menyediakan repo bertandatangan (tiada apt-key — alat tersebut sudah tiada):
curl -fsSL https://deb.nodesource.com/setup_24.x | sudo -E bash -
sudo apt install -y nodejs
node --versionBahagian yang sering disalah faham: pasang CLI sebagai pengguna agent anda, jangan sekali-kali menggunakan sudo npm -g. Prefix global milik root akan menyebabkan ralat keizinan (permission errors) kemudian dan meninggalkan fail milik root dalam cache npm. Tetapkan prefix npm ke direktori home pengguna terlebih dahulu:
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 --versionArahan export diletakkan dalam ~/.bashrc, bukan ~/.profile, dan ia mesti berada di atas baris kawalan "If not running interactively, don't do anything" di bahagian atas fail: tmux mungkin memulakan shell bukan login, yang membaca ~/.bashrc dan melangkau ~/.profile — ~/.profile hanya berjalan untuk shell login. Menggunakan Node bagi setiap pengguna melalui pengurus versi seperti nvm memberikan hasil yang sama; matlamatnya adalah supaya npm install -g tidak memerlukan sudo. npm masih berfungsi dengan baik, atau gunakan skrip pemasangan asli Anthropic, yang merupakan tetapan lalai yang didokumentasikan sekarang. Semak dokumentasi pemasangan Anthropic sebelum anda menampal — kaedah pemasangan sentiasa berubah.
Jalankan claude di dalam repo untuk memulakannya. Pelaksanaan pertama akan membimbing anda melalui proses pengesahan; pelayan tanpa paparan (headless) tidak mempunyai pelayar, jadi proses tersebut akan memberikan URL untuk dibuka pada mesin anda sendiri dan kod untuk dimasukkan semula ke terminal. (Menggunakan kunci API dalam persekitaran adalah cara alternatif.) Dalam kedua-dua cara, kredensial tersebut kini tersimpan pada pelayan — yang membawa kita kepada bahagian yang sering diabaikan oleh pengguna.
Perbincangan mengenai blast radius
Ejen dengan akses shell adalah sebuah shell. Ia boleh membaca apa sahaja yang boleh dibaca oleh pengguna yang menjalankannya, dan menghantar data ke mana sahaja pengguna tersebut boleh menghantar. Ini bukan kritikan terhadap alatan tersebut, tetapi merupakan definisinya — dan inilah sebabnya akaun yang menjalankannya adalah lebih penting daripada mana-mana tetapan individu.
- Pengguna khusus yang tidak mempunyai keistimewaan. Tiada kumpulan
sudo, tiada direktori home yang dikongsi dengan akaun anda sendiri. - Tiada kredensial produksi pada mesin tersebut. Tiada
~/.aws/credentialsyang menyimpan kunci prod, tiada.envyang disalin dari produksi, tiada kata laluan pangkalan data dengan akses tulis ke mana-mana yang penting. Berikan ejen kredensial staging atau akses baca sahaja. - Token berskop. Token GitHub yang terperinci dan terhad kepada satu repositori; kunci deploy apabila akses baca sudah mencukupi.
Claude Code menyediakan flag yang melangkau semua permintaan kebenaran sepenuhnya. Pada komputer riba, untuk projek sementara, itu adalah pilihan anda. Pada pelayan yang menyimpan token, ia menghapuskan penghalang terakhir antara arahan yang salah baca dengan git push --force. Apa yang sebenarnya diubah oleh flag tersebut, dan cara untuk mengehadkan ejen yang menjalankannya, daripada sandbox terbina dalam sehinggalah ke VPS yang boleh dibuang, dibincangkan dalam menjalankan Claude Code dengan selamat pada pelayan.
Deploy key vs SSH agent forwarding
Adalah mudah untuk melakukan ssh -A supaya git boleh menggunakan kunci pada komputer riba anda. Fahami apa yang diberikan oleh tindakan tersebut: agent forwarding mendedahkan socket SSH agent tempatan anda kepada proses yang berjalan sebagai pengguna tersebut pada mesin tersebut. Apa sahaja yang berjalan sebagai agent — termasuk ejen tersebut — boleh meminta kunci anda untuk menandatangani bagi mana-mana hos yang boleh dicapai, selagi anda kekal bersambung. Itu adalah jauh lebih besar daripada sekadar "biarkan git pull satu repo ini sahaja".
Sebaliknya, jana kunci pada pelayan, daftarkannya sebagai deploy key bagi setiap repositori (akses tulis hanya jika ejen perlu melakukan push), dan tetapkan identiti git supaya komit dari mesin tersebut boleh dikenali:
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"Aliran kerja tmux
Pasang sudo apt install tmux, kemudian buat ~/.tmux.conf yang minimal:
set -g mouse on
set -g history-limit 50000
set -g default-terminal "tmux-256color"Empat arahan ini merangkumi penggunaan harian:
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 claudetmux new -A -s claude adalah arahan yang perlu dihafal — ia akan menyambung (attach) jika sesi wujud dan mencipta sesi baharu jika tidak wujud. Jadi, satu arahan ini merangkumi proses memulakan kerja dan menyambung semula selepas terputus sambungan. Gunakan alias untuknya. Di dalam sesi, Ctrl-b c membuka tetingkap, Ctrl-b n dan Ctrl-b p menukar tetingkap tersebut, manakala Ctrl-b [ memasuki mod salin untuk skrol ke atas (q untuk keluar).
Satu perkara penting tentang sesi yang tidak ditutup: ejen akan menghantar semula keseluruhan perbualan pada setiap pusingan. Sila baca apa yang digunakan oleh sesi Claude Code yang lama untuk tokennya sebelum anda membiarkan sesi berjalan selama seminggu.
Failure modes
"My session is gone." tmux ls memaparkan no server running on /tmp/tmux-1000/default. Ini hampir selalunya bermaksud proses tersebut tidak pernah berada di dalam tmux — anda masuk melalui SSH, menjalankan claude secara terus, dan pemutusan sambungan telah menghentikannya. Tiada apa yang boleh dipulihkan. Tabiat untuk mengelakkannya: tmux new -A -s <project> adalah arahan pertama selepas setiap log masuk.
Pane mengecil menjadi kotak kecil. tmux menetapkan saiz sesi mengikut klien terkecil yang disambungkan, jadi klien lama yang masih disambungkan dari mesin lain akan mengecilkan paparan. Paksa klien lain keluar semasa anda menyambung: tmux attach -d -t claude.
Build memaparkan Killed. Hanya satu perkataan, tanpa stack trace. Sahkan dengan sudo dmesg -T | grep -i -E 'out of memory|killed process' — kernel OOM killer telah memilih proses terbesar. Dari Node, anda mungkin melihat FATAL ERROR: Ineffective mark-compacts near heap limit Allocation failed - JavaScript heap out of memory sebagai ganti. Cara penyelesaian, mengikut urutan: tambah swap (di atas), hadkan ujian dan keperituan (parallelism) kompiler, tingkatkan heap Node dengan NODE_OPTIONS=--max-old-space-size=..., atau naik taraf saiz VPS. OOM killer juga mungkin memilih tmux server berbanding build, yang akan menyebabkan sesi anda hilang; jika systemd-oomd sedang berjalan, ia boleh mematikan keseluruhan user slice dengan kesan yang sama.
npm error code EACCES / permission denied, mkdir '/usr/lib/node_modules/...'. Pemasangan global ke dalam prefix milik root. Gunakan prefix ~/.npm-global di atas. Jika anda telah menjalankan sudo npm sebelum ini, anda mungkin juga melihat Your cache folder contains root-owned files — baiki dengan sudo chown -R $(id -u):$(id -g) ~/.npm.
claude: command not found — tetapi hanya kadangkala. Eksport PATH anda berada di dalam ~/.bashrc di bawah perlindungan "If not running interactively, don't do anything", jadi shell bukan interaktif akan melangkauinya. Alihkan eksport tersebut ke atas perlindungan itu dan simpan di dalam ~/.bashrc, bukan ~/.profile: tmux mungkin memulakan shell bukan login, yang membaca ~/.bashrc dan tidak menyentuh ~/.profile.
Warna menjadi kacau selepas menyambung. Ketidakpadanan TERM — baris default-terminal di atas adalah penyelesaiannya.
Sesi hilang selepas reboot. Bukan pepijat: tmux server adalah sebuah proses, dan reboot akan menghentikannya. Semak uptime.
Apa yang akan terjejas apabila skala meningkat
Lebih banyak projek. Satu sesi tmux bagi setiap repo, dinamakan mengikut nama repo tersebut; tmux ls kemudiannya menjadi papan pemuka anda. Jika anda mengabaikan disiplin penamaan ini, anda akan mendapat sesi 0, 1, 2. Port juga akan menjadi tidak teratur — jika enam repo semuanya memerlukan :3000, anda perlu berhenti menetapkan port secara manual dan biarkan traefik reverse proxy menguruskan laluan pelbagai aplikasi di bawah Docker Compose melakukan penghantaran melalui hostname.
Lebih ramai pengguna. Sockets tmux adalah mengikut pengguna, jadi dua pembangun pada mesin yang sama akan mendapat pelayan tmux masing-masing dan tidak dapat melihat sesi satu sama lain. Berkongsi satu sesi melalui socket yang dikongsi bermaksud semua orang menaip ke dalam shell yang sama sebagai pengguna Unix yang sama, dengan kesan audit dan keizinan yang berkaitan. Menggunakan pengguna berasingan adalah penyelesaian yang betul dan paling mudah.
Kerja tanpa pengawasan. tmux adalah untuk sesi interaktif yang anda sambung. Tugasan yang berjalan mengikut jadual tanpa pengawasan harus diletakkan dalam unit systemd dan timer, di mana tugasan tersebut akan mendapat log, polisi pemulihan, dan kelangsungan selepas but secara percuma. Menggunakan tmux untuk menjalankan tugasan jenis cron adalah tanda bahawa tugasan tersebut sepatutnya menjadi sebuah servis.
Satu nota terakhir: ikat dev server supaya agent bermula pada 127.0.0.1, bukan 0.0.0.0, dan akses mereka melalui terowong SSH (ssh -L 3000:127.0.0.1:3000 agent@your-server) berbanding membuka port dalam ufw. Apabila anda mula memforward lebih daripada enam port, atau apabila telefon dan komputer riba mahukan pratonton yang sama, gunakan VPN WireGuard hos sendiri pada VPS di hadapannya: dev server akan diikat pada antara muka peribadi, dan ufw akan terus menyekat semua perkara daripada antara muka awam. Firewall hanya membantu jika anda berhenti membuka lubang padanya.
Claude Code bukan satu-satunya pilihan: menjalankan coding AI agent pada VPS juga melibatkan Aider dan Goose.
FAQ
Adakah Claude Code terus berjalan selepas sambungan SSH saya terputus?
Hanya jika anda memulakannya di dalam tmux. Proses yang dilancarkan terus dari shell SSH adalah anak kepada shell tersebut dan akan mati apabila pty terputus. Di dalam tmux, shell tersebut milik pelayan tmux yang telah diasingkan (detached), jadi ejen akan terus bekerja di tengah tugasan dan tmux attach akan membawa anda kembali ke skrol yang sama. Jadikan tmux new -A -s <project> sebagai arahan pertama selepas setiap log masuk untuk menyelesaikan masalah ini.
Patutkah saya memasang CLI dengan sudo npm install -g?
Tidak. Prefix global milik root akan menyebabkan ralat EACCES semasa pemasangan seterusnya dan fail milik root dalam cache npm. Tetapkan prefix npm ke ~/.npm-global (atau gunakan pengurus versi seperti nvm), pasang sebagai pengguna agent tanpa keistimewaan, dan eksport ~/.npm-global/bin ke PATH daripada ~/.bashrc, di atas guard interaktif. Jika anda telah menjalankan sudo npm sebelum ini, baiki cache dengan sudo chown -R $(id -u):$(id -g) ~/.npm.
Adakah ssh -A agent forwarding selamat pada mesin yang menjalankan ejen?
Ia memberikan lebih banyak akses daripada yang diperlukan oleh tugasan tersebut. Forwarding mendedahkan soket SSH agent tempatan anda kepada setiap proses yang berjalan sebagai pengguna tersebut, jadi apa-apa sahaja pada mesin itu boleh meminta kunci anda untuk menandatangani sesi bagi mana-mana hos yang boleh dicapai selagi anda kekal bersambung. Jana kunci ed25519 pada pelayan dan daftarkannya sebagai kunci deploy bagi setiap repositori, dengan akses tulis hanya jika ejen perlu melakukan push.
Mengapa binaan (build) saya hanya mencetak Killed?
Satu perkataan tanpa stack trace bermaksud kernel OOM killer. Sahkan dengan sudo dmesg -T | grep -i -E 'out of memory|killed process'; daripada Node anda mungkin melihat JavaScript heap out of memory sebagai ganti. Lakukan pembaikan mengikut urutan: tambah swapfile, hadkan ujian dan paralelisasi kompilator, naikkan NODE_OPTIONS=--max-old-space-size=..., kemudian besarkan saiz VPS. Berhati-hati kerana OOM killer boleh memilih pelayan tmux berbanding proses binaan, yang akan mematikan keseluruhan sesi anda.
tmux atau perkhidmatan systemd?
tmux sesuai untuk sesi interaktif yang anda sambung, pantau dan taip, yang merupakan fungsi utama sesi ejen. Kerja yang berjalan mengikut jadual tanpa pemantauan harus diletakkan dalam unit dan pemasa systemd, di mana log, polisi pemulihan (restart policy) dan kelangsungan but (boot survival) disediakan secara automatik. Jika anda menggunakan tmux untuk menjalankan tugasan jenis cron, tugasan tersebut sepatutnya menjadi perkhidmatan (service).