Cara menjalankan Claude Code di VPS dengan tmux
Gunakan tmux di Linux VPS agar sesi Claude Code tidak mati saat koneksi SSH terputus. Pelajari cara instalasi dan cara menjaga agen tetap berjalan stabil.
Masalahnya adalah penutup laptop, bukan CLI
Claude Code berjalan dengan baik di laptop Anda sampai Anda menutupnya: sesi SSH terputus, shell menerima SIGHUP, dan agen mati saat sedang menjalankan pengujian setelah tiga menit. Jalankan CLI pada mesin yang tidak pernah masuk ke mode sleep, di dalam terminal multiplexer yang prosesnya bukan merupakan child dari sesi SSH Anda. Itulah inti solusinya — dan tmux, bukan instalasi, adalah bagian yang paling krusial.
Halaman ini membahas tentang pengoperasian mesin yang digunakan untuk menjalankan agen secara terus-menerus. Jika Anda tidak memiliki server Linux yang dapat dibiarkan menyala, maka semua instruksi ini tidak berlaku. Itu adalah satu-satunya prasyarat utama.
Apa yang sebenarnya dilakukan tmux
Saat Anda melakukan SSH, sshd melakukan fork pada shell dan memberikan pseudo-terminal; semua yang Anda jalankan dari shell tersebut adalah child dari shell tersebut. Jika koneksi terputus, kernel akan menghapus pty, shell menerima SIGHUP, dan shell tersebut akan memutus koneksi ke child-nya. Proses foreground yang berjalan lama akan mati.
tmux membalikkan kepemilikan tersebut. Perintah tmux yang Anda ketik adalah client tipis yang berkomunikasi melalui unix socket ke tmux server yang berjalan terpisah dari terminal Anda. Shell di dalam sebuah sesi adalah child dari server tersebut, bukan dari sshd. Jika koneksi SSH terputus, client akan hilang, namun server, sesi, dan agent tetap berjalan di tengah tugas. Hubungkan kembali, tmux attach, dan Anda akan kembali ke shell yang sama dengan riwayat scrollback yang sama. nohup juga bertahan setelah koneksi terputus, tetapi tidak memberikan cara untuk masuk kembali — Anda tidak dapat melakukan re-attach ke TUI yang berjalan di background. Claude Code bersifat interaktif; tmux (atau screen) adalah alat yang tepat.
Menentukan ukuran server
CLI adalah proses Node; CLI bukan beban utama mesin. Beban utama mesin berasal dari apa pun yang dijalankan oleh agent untuk Anda: proses build, rangkaian pengujian lengkap, tsc, language server, atau database di dalam Docker. Tentukan ukuran berdasarkan toolchain, bukan CLI. Tambahkan swap meskipun Anda tidak berencana menggunakannya — ini mengubah proses OOM kill yang fatal menjadi proses build yang lambat:
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 juga penggunaan disk: repositori, node_modules, dan image Docker akan menumpuk dengan cepat. Jika toolchain menggunakan sumber daya melampaui container hingga ke virtual machine penuh — seperti guest KVM atau node Kubernetes lokal — pastikan paket layanan Anda mendukung ekstensi virtualisasi CPU sebelum Anda berlangganan, karena menjalankan nested virtualization pada VPS adalah fitur yang diaktifkan oleh penyedia layanan, bukan sesuatu yang dapat Anda aktifkan dari dalam guest.
Pengguna non-root terlebih dahulu
Buat pengguna khusus dengan direktori home sendiri, lalu letakkan kunci publik Anda:
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_keysSengaja, agent tidak berada dalam grup sudo. Jika paket sistem diperlukan, Anda harus menginstalnya. Keputusan tersebut menghilangkan sebagian besar cara perintah shell yang tidak terduga dapat merusak host.
Higienitas SSH untuk server yang terus menyala
Autentikasi kata sandi pada mesin yang terhubung ke internet publik sepanjang hari, yang menyimpan agent dan kode sumber Anda, adalah risiko yang tidak perlu. Matikan fitur tersebut. Pada Ubuntu 24.04 dan Debian 13, /etc/ssh/sshd_config menyertakan /etc/ssh/sshd_config.d/*.conf, jadi buatlah file baru daripada mengedit konfigurasi utama:
# /etc/ssh/sshd_config.d/10-hardening.conf
PasswordAuthentication no
KbdInteractiveAuthentication no
PermitRootLogin noValidasi dan muat ulang — biarkan sesi Anda saat ini tetap terbuka saat Anda menguji sesi baru dari terminal kedua:
sudo sshd -t && sudo systemctl restart sshDetail teknis pada Ubuntu 24.04: sshd menggunakan aktivasi socket. Pengaturan autentikasi berlaku pada systemctl restart ssh, tetapi perubahan pada Port memerlukan systemctl daemon-reload dan restart pada ssh.socket.
Kemudian firewall. Izinkan SSH sebelum Anda mengaktifkannya, atau Anda akan terkunci dari sistem:
sudo ufw allow OpenSSH
sudo ufw default deny incoming
sudo ufw default allow outgoing
sudo ufw enableInstal fail2ban dengan pertimbangan matang mengenai manfaatnya: setelah autentikasi kata sandi dimatikan, serangan brute force tidak akan berhasil — fitur ini menjaga log journal tetap bersih dari upaya gagal.
# /etc/fail2ban/jail.local
[sshd]
enabled = true
backend = systemd
maxretry = 5
bantime = 1hTerakhir, lakukan pembaruan otomatis dengan sudo apt install unattended-upgrades dan sudo dpkg-reconfigure -plow unattended-upgrades. Perhatikan interaksi dengan tmux: jika Unattended-Upgrade::Automatic-Reboot aktif dan pembaruan kernel melakukan reboot, server akan memuat ulang dan memutus semua sesi. Biarkan fitur ini nonaktif dan lakukan reboot sesuai jadwal Anda, saat tidak ada proses yang sedang berjalan.
Install Node.js and Claude Code di Ubuntu
Claude Code adalah Node CLI, jadi Anda memerlukan Node versi terbaru. Paket distro sering kali tertinggal; NodeSource adalah jalur umum di Ubuntu dan Debian, dan menyediakan repositori bertanda tangan (tanpa apt-key — alat tersebut sudah tidak ada):
curl -fsSL https://deb.nodesource.com/setup_24.x | sudo -E bash -
sudo apt install -y nodejs
node --versionBagian yang sering salah dilakukan orang: instal CLI sebagai pengguna agent Anda, jangan pernah menggunakan sudo npm -g. Prefix global milik root akan menyebabkan error izin akses di kemudian hari dan meninggalkan file milik root di cache npm. Arahkan 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 --versionPerintah export diletakkan di ~/.bashrc, bukan ~/.profile, dan harus berada di atas baris penjaga "If not running interactively, don't do anything" di bagian atas file: tmux mungkin menjalankan shell non-login, yang membaca ~/.bashrc dan melewati ~/.profile — ~/.profile hanya berjalan untuk shell login. Menggunakan Node per-pengguna melalui manajer versi seperti nvm memberikan hasil yang sama; tujuannya adalah agar npm install -g tidak memerlukan sudo. npm tetap berfungsi dengan baik, atau gunakan skrip instalasi asli Anthropic, yang merupakan default yang didokumentasikan saat ini. Periksa dokumentasi instalasi Anthropic sebelum Anda menempelkan perintah — metode instalasi dapat berubah.
Jalankan claude di dalam repositori untuk memulainya. Jalankan pertama kali akan memandu Anda melalui autentikasi; server headless tidak memiliki browser, sehingga alur tersebut akan memberikan URL untuk dibuka di mesin Anda sendiri dan sebuah kode untuk dimasukkan kembali ke terminal. (Menggunakan API key di environment adalah jalur lainnya.) Dengan cara apa pun, kredensial tersebut kini tersimpan di server — yang membawa kita ke bagian yang sering dilewati orang.
Pembahasan mengenai blast radius
Agen dengan akses shell adalah sebuah shell. Agen tersebut dapat membaca apa pun yang dapat dibaca oleh pengguna yang menjalankannya, dan dapat melakukan push ke mana pun pengguna tersebut dapat melakukan push. Ini bukan kritik terhadap alat tersebut, melainkan definisinya — dan inilah alasan mengapa akun yang menjalankan agen tersebut lebih penting daripada pengaturan individu apa pun.
- Pengguna khusus yang tidak memiliki hak istimewa. Tanpa grup
sudo, dan tidak ada direktori home yang digunakan bersama dengan akun Anda sendiri. - Tidak ada kredensial produksi di dalam mesin. Tidak ada
~/.aws/credentialsyang menyimpan kunci produksi, tidak ada.envyang disalin dari produksi, dan tidak ada kata sandi database dengan hak akses tulis ke aset penting apa pun. Berikan agen kredensial staging atau kredensial read-only. - Token dengan cakupan terbatas. Token GitHub fine-grained yang dibatasi hanya pada satu repositori; atau deploy key jika akses baca sudah cukup.
Claude Code menyediakan flag yang melewati seluruh permintaan izin. Pada laptop atau pada proyek sementara, itu adalah keputusan Anda. Namun pada server yang menyimpan token, flag tersebut menghilangkan penghalang terakhir antara instruksi yang salah baca dengan git push --force. Apa yang sebenarnya diubah oleh flag tersebut, dan cara membatasi agen yang menjalankannya, mulai dari sandbox bawaan hingga VPS sekali pakai, dibahas dalam menjalankan Claude Code secara aman di server.
Deploy key vs SSH agent forwarding
Sangat menggoda untuk melakukan ssh -A agar git dapat menggunakan kunci pada laptop Anda. Pahami apa yang diberikan oleh tindakan tersebut: agent forwarding mengekspos socket SSH agent lokal Anda ke proses yang berjalan sebagai pengguna tersebut di dalam mesin. Apa pun yang berjalan sebagai agent — termasuk agen tersebut — dapat meminta kunci Anda untuk melakukan tanda tangan untuk host apa pun yang dapat dijangkau, selama Anda tetap terhubung. Itu jauh lebih luas daripada sekadar "izinkan git melakukan pull pada satu repositori ini".
Sebagai gantinya, buatlah kunci di dalam server, daftarkan sebagai deploy key per-repositori (hanya berikan hak akses tulis jika agen perlu melakukan push), dan atur identitas git agar commit dari mesin tersebut dapat 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"Alur kerja tmux
Instal aplikasi ini (sudo apt install tmux), lalu buat ~/.tmux.conf minimal:
set -g mouse on
set -g history-limit 50000
set -g default-terminal "tmux-256color"Empat perintah ini mencakup penggunaan sehari-hari:
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 perintah yang wajib dihafal — perintah ini akan menyambungkan sesi jika sudah ada, atau membuat sesi baru jika belum ada. Satu perintah ini dapat digunakan untuk memulai sesi baru maupun melanjutkan sesi yang terputus. Gunakan alias untuk perintah ini. Di dalam sesi, Ctrl-b c membuka jendela baru, Ctrl-b n dan Ctrl-b p digunakan untuk berpindah antar jendela, dan Ctrl-b [ masuk ke mode salin untuk menggulir ke atas (q untuk keluar).
Satu hal penting mengenai sesi yang tidak pernah dihentikan: agen akan mengirim ulang seluruh percakapan pada setiap giliran. Baca apa yang menghabiskan token pada sesi Claude Code yang berjalan lama sebelum membiarkan sesi tetap berjalan selama satu minggu.
Failure modes
"My session is gone." tmux ls mencetak no server running on /tmp/tmux-1000/default. Ini hampir selalu berarti proses tersebut tidak pernah berjalan di dalam tmux — Anda masuk via SSH, menjalankan claude secara langsung, dan pemutusan koneksi menghentikan proses tersebut. Tidak ada yang bisa dipulihkan. Kebiasaan untuk mencegahnya: tmux new -A -s <project> adalah perintah pertama setelah setiap login.
Pane menyusut menjadi kotak kecil. tmux menyesuaikan ukuran sesi dengan klien terkecil yang terhubung, sehingga klien lama yang masih terhubung dari mesin lain akan mempersempit tampilan. Paksa klien lain keluar saat Anda terhubung: tmux attach -d -t claude.
Build mencetak Killed. Hanya satu kata, tanpa stack trace. Konfirmasi dengan sudo dmesg -T | grep -i -E 'out of memory|killed process' — kernel OOM killer memilih proses terbesar. Pada Node, Anda mungkin melihat FATAL ERROR: Ineffective mark-compacts near heap limit Allocation failed - JavaScript heap out of memory sebagai gantinya. Solusi, secara berurutan: tambahkan swap (di atas), batasi pengujian dan paralelisme kompilator, naikkan heap Node dengan NODE_OPTIONS=--max-old-space-size=..., atau tingkatkan spesifikasi VPS. OOM killer juga dapat memilih tmux server alih-alih proses build, sehingga sesi Anda ikut hilang; jika systemd-oomd sedang berjalan, ia dapat mematikan seluruh user slice dengan efek yang sama.
npm error code EACCES / permission denied, mkdir '/usr/lib/node_modules/...'. Instalasi global ke dalam prefix milik root. Gunakan prefix ~/.npm-global di atas. Jika Anda sudah menjalankan sudo npm sebelumnya, Anda mungkin juga melihat Your cache folder contains root-owned files — perbaiki dengan sudo chown -R $(id -u):$(id -g) ~/.npm.
claude: command not found — tetapi hanya terkadang. Ekspor PATH Anda berada di dalam ~/.bashrc di bawah proteksi "If not running interactively, don't do anything", sehingga shell non-interaktif akan melewatkannya. Pindahkan ekspor tersebut ke atas proteksi tersebut dan simpan di ~/.bashrc, bukan ~/.profile: tmux dapat menjalankan non-login shells, yang membaca ~/.bashrc dan tidak pernah menyentuh ~/.profile.
Warna berantakan setelah terhubung. Ketidakcocokan TERM — baris default-terminal di atas adalah solusinya.
Sesi hilang setelah reboot. Bukan bug: tmux server adalah sebuah proses, dan reboot akan menghentikannya. Periksa uptime.
Apa yang rusak saat skala meningkat
Lebih banyak proyek. Gunakan satu sesi tmux per repositori dengan nama sesuai repositori tersebut; tmux ls kemudian menjadi dashboard Anda. Jika Anda mengabaikan disiplin penamaan, Anda akan mendapatkan sesi 0, 1, 2. Port juga akan berantakan dengan cara yang sama — jika enam repositori semuanya membutuhkan :3000, berhentilah menetapkan port secara manual dan biarkan a Traefik reverse proxy route multiple apps under Docker Compose melakukan pengiriman melalui hostname.
Lebih banyak orang. Socket tmux bersifat per-user, sehingga dua pengembang pada mesin yang sama masing-masing mendapatkan server tmux sendiri dan tidak dapat melihat sesi satu sama lain. Berbagi satu sesi melalui socket bersama berarti semua orang mengetik ke dalam shell yang sama sebagai user Unix yang sama, dengan konsekuensi audit dan izin yang menyertainya. Menggunakan user terpisah adalah solusi yang membosankan namun benar.
Pekerjaan otomatis. tmux ditujukan untuk sesi interaktif yang Anda hubungkan. Pekerjaan yang berjalan sesuai jadwal tanpa pengawasan harus berada dalam unit dan timer systemd, di mana pekerjaan tersebut mendapatkan logging, kebijakan restart, dan ketahanan saat boot secara otomatis. Menggunakan tmux untuk menjalankan pekerjaan tipe cron adalah tanda bahwa pekerjaan tersebut seharusnya menjadi sebuah service.
Satu catatan terakhir: hubungkan dev server agar agent mulai melakukan 127.0.0.1, bukan 0.0.0.0, dan akses melalui SSH tunnel (ssh -L 3000:127.0.0.1:3000 agent@your-server) daripada membuka port di ufw. Jika Anda sudah meneruskan setengah lusin port, atau jika ponsel dan laptop keduanya membutuhkan preview yang sama, gunakan self-hosted WireGuard VPN on the VPS di depannya: dev server akan terhubung ke interface privat, dan ufw akan tetap menolak semua koneksi dari interface publik. Firewall hanya berguna jika Anda berhenti membuka celah pada firewall tersebut.
Claude Code bukan satu-satunya pilihan: running a coding AI agent on a VPS juga mempertimbangkan Aider dan Goose.
FAQ
Apakah Claude Code tetap berjalan setelah koneksi SSH saya terputus?
Hanya jika Anda menjalankannya di dalam tmux. Proses yang diluncurkan langsung dari shell SSH adalah anak dari shell tersebut dan akan mati bersama pty saat koneksi terputus. Di dalam tmux, shell tersebut milik server tmux yang terlepas (detached), sehingga agen tetap bekerja di tengah tugas dan tmux attach akan mengembalikan Anda ke scrollback yang sama. Gunakan tmux new -A -s <project> sebagai perintah pertama setelah setiap login untuk mengatasi masalah ini.
Haruskah saya menginstal CLI dengan sudo npm install -g?
Tidak. Prefix global milik root akan menyebabkan error EACCES pada instalasi berikutnya dan file milik root di cache npm. Atur prefix npm ke ~/.npm-global (atau gunakan manajer versi seperti nvm), instal sebagai user agent tanpa hak akses root, dan ekspor ~/.npm-global/bin ke PATH dari ~/.bashrc, di atas guard interaktif. Jika Anda sudah menjalankan sudo npm sebelumnya, perbaiki cache dengan sudo chown -R $(id -u):$(id -g) ~/.npm.
Apakah ssh -A agent forwarding aman pada mesin yang menjalankan agen?
Ini memberikan izin jauh lebih besar daripada yang dibutuhkan oleh pekerjaan tersebut. Forwarding mengekspos socket SSH agent lokal Anda ke setiap proses yang berjalan sebagai user tersebut, sehingga apa pun di mesin tersebut dapat meminta kunci Anda untuk menandatangani akses ke host mana pun yang dapat dijangkau selama Anda tetap terhubung. Buat kunci ed25519 di server dan daftarkan sebagai deploy key per-repositori, dengan akses tulis hanya jika agen memang harus melakukan push.
Mengapa build saya hanya mencetak Killed?
Satu kata tanpa stack trace adalah kernel OOM killer. Konfirmasikan dengan sudo dmesg -T | grep -i -E 'out of memory|killed process'; dari Node Anda mungkin melihat JavaScript heap out of memory sebagai gantinya. Lakukan perbaikan berikut secara berurutan: tambahkan swapfile, batasi paralelisme test dan compiler, naikkan NODE_OPTIONS=--max-old-space-size=..., lalu tingkatkan ukuran VPS. Waspadai bahwa OOM killer dapat memilih server tmux alih-alih proses build, yang akan mematikan seluruh sesi Anda.
tmux atau layanan systemd?
tmux cocok untuk sesi interaktif yang Anda sambungkan, pantau, dan ketik, yang mana merupakan fungsi utama dari sesi agen. Pekerjaan yang berjalan sesuai jadwal tanpa pengawasan harus menggunakan unit dan timer systemd, di mana logging, kebijakan restart, dan ketahanan saat boot sudah tersedia secara otomatis. Jika Anda menggunakan tmux untuk menjalankan pekerjaan berbasis cron, pekerjaan tersebut seharusnya menjadi sebuah layanan (service).