SSD Nodes Learn Hosting plans →
Panduan Matt ConnorOleh Matt Connor

Menjalankan Claude Code di VPS dengan tmux

Pelajari cara menjalankan Claude Code di VPS Linux yang selalu aktif dengan tmux agar sesi agent tetap berjalan setelah koneksi SSH terputus, termasuk instalasi dan kendala umum.

Masalahnya adalah penutup laptop, bukan CLI

Claude Code berjalan baik di laptop Anda sampai penutupnya ditutup: sesi SSH terputus, shell menerima SIGHUP, dan agent yang baru tiga menit menjalankan pengujian ikut berhenti. Jalankan CLI pada mesin yang tidak pernah masuk mode tidur, di dalam terminal multiplexer yang prosesnya bukan child dari sesi SSH Anda. Itulah satu-satunya trik yang diperlukan. Bagian pentingnya adalah tmux, bukan proses instalasi.

Halaman ini membahas cara mengoperasikan mesin yang digunakan untuk menjalankan agent. Jika Anda tidak memiliki server Linux yang dapat dibiarkan menyala, tidak ada bagian dari panduan ini yang berlaku. Itulah satu-satunya prasyarat yang perlu disampaikan secara jelas.

Apa yang sebenarnya dilakukan tmux

Saat Anda login melalui SSH, sshd membuat proses shell dan memberinya pseudo-terminal; semua proses yang Anda jalankan dari shell tersebut menjadi proses turunannya. Jika koneksi terputus, kernel menghentikan pty, shell menerima SIGHUP, lalu shell meneruskan sinyal hangup kepada proses turunannya. Proses foreground yang berjalan lama akan berhenti.

tmux membalikkan hubungan kepemilikan ini. Perintah tmux yang Anda ketik adalah thin client yang berkomunikasi melalui unix socket dengan server tmux yang berjalan terpisah dari terminal Anda. Shell di dalam session merupakan proses turunan server tersebut, bukan proses turunan sshd. Jika koneksi SSH dihentikan, client keluar, tetapi server, session, dan agent yang sedang menjalankan tugas tetap berjalan. Hubungkan kembali, jalankan tmux attach, dan Anda kembali ke shell yang sama dengan scrollback yang sama. nohup juga tetap berjalan setelah hangup, tetapi tidak menyediakan cara untuk masuk kembali; Anda tidak dapat melakukan re-attach ke TUI yang berjalan di background. Claude Code bersifat interaktif; tmux (atau screen) adalah tool yang tepat.

Menentukan kapasitas server

CLI itu sendiri adalah satu binary native; CLI bukan penyebab utama kapasitas mesin cepat habis. Yang menghabiskan kapasitas mesin adalah apa pun yang dijalankan agent untuk Anda: build, rangkaian pengujian lengkap, tsc, language server, atau database dalam Docker. Tentukan kapasitas berdasarkan toolchain, bukan CLI. Tambahkan swap meskipun Anda berencana tidak pernah menggunakannya. Swap mengubah penghentian paksa akibat OOM menjadi build yang berjalan 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/fstab

Pantau juga kapasitas disk. Repositori, node_modules, dan image Docker cepat menumpuk. Jika toolchain menggunakan lebih dari sekadar container hingga menjalankan virtual machine penuh, misalnya guest KVM atau node Kubernetes lokal, pastikan paket yang dipilih mengekspos ekstensi virtualisasi CPU sebelum Anda berlangganan. Menjalankan nested virtualization pada VPS bergantung pada pengaktifan oleh provider, bukan sesuatu yang dapat Anda aktifkan dari dalam guest.

Pengguna non-root terlebih dahulu

Buat pengguna khusus dengan direktori home sendiri, lalu tempatkan 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_keys

Uji login dari terminal kedua sebelum melanjutkan, saat autentikasi kata sandi masih tersedia sebagai cadangan. Jika muncul Akses ditolak (publickey), penyebabnya biasanya adalah kepemilikan atau mode pada direktori .ssh, bukan kuncinya.

Secara sengaja, agent tidak termasuk dalam grup sudo. Jika diperlukan paket sistem, instal paket tersebut. Keputusan ini menghilangkan sebagian besar kemungkinan perintah shell yang tidak semestinya merusak host.

Kebersihan SSH untuk server yang dibiarkan aktif

Autentikasi kata sandi pada mesin yang terus terhubung ke Internet publik sepanjang hari, serta menyimpan agent dan kode sumber Anda, merupakan risiko yang tidak perlu ditanggung. Nonaktifkan autentikasi tersebut. Pada Ubuntu 24.04 dan Debian 13, /etc/ssh/sshd_config menyertakan /etc/ssh/sshd_config.d/*.conf, jadi buat file tambahan alih-alih mengedit konfigurasi utama:

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

Jangan masukkan pertukaran kunci ke dalam file tersebut kecuali ada alasan khusus: baris KexAlgorithms yang disalin dari panduan hardening lama sering kali hanya menetapkan algoritme klasik. Akibatnya, ssh client terbaru dapat menampilkan peringatan bahwa koneksi tidak menggunakan pertukaran kunci pascakuantum.

Validasi dan muat ulang konfigurasi. Biarkan sesi Anda saat ini tetap terbuka sambil menguji sesi baru dari terminal kedua:

sudo sshd -t && sudo systemctl restart ssh

Ada satu hal khusus pada Ubuntu 24.04: sshd diaktifkan melalui socket. Pengaturan autentikasi berlaku pada systemctl restart ssh, tetapi perubahan pada Port yang menangani koneksi juga memerlukan systemctl daemon-reload dan restart ssh.socket.

Berikutnya, konfigurasi firewall. Izinkan SSH sebelum mengaktifkan firewall, atau Anda akan terkunci di luar server:

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

Instal fail2ban dengan memahami manfaatnya: setelah autentikasi kata sandi dinonaktifkan, brute force tidak akan berhasil; fitur ini hanya mencegah percobaan yang gagal memenuhi journal Anda.

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

Terakhir, lakukan patch otomatis dengan sudo apt install unattended-upgrades dan sudo dpkg-reconfigure -plow unattended-upgrades. Perhatikan interaksinya dengan tmux: aktifkan Unattended-Upgrade::Automatic-Reboot dan pembaruan kernel akan me-reboot server, sehingga semua sesi ikut terputus. Biarkan fitur tersebut nonaktif dan lakukan reboot sesuai jadwal Anda sendiri, saat tidak ada proses yang sedang berjalan. Hal yang sama berlaku untuk upgrade rilis: memindahkan server dari Ubuntu 24.04 ke 26.04 akan me-restart sshd dan kernel, jadi lakukan dalam jadwal pemeliharaan saat tidak ada sesi tmux yang menjalankan pekerjaan penting.

Instal Node.js dan Claude Code di Ubuntu

Rute npm memerlukan Node.js 22 atau yang lebih baru. (Paket ini sekarang menyertakan biner native yang tidak berjalan pada Node, tetapi npm tetap memeriksa versi engine, dan toolchain milik agent biasanya juga memerlukan Node versi terbaru.) Paket dari distro sering tertinggal; NodeSource merupakan rute yang umum digunakan pada Ubuntu dan Debian, serta menyediakan repo bertanda tangan (tanpa apt-key, yang sudah deprecated pada Ubuntu 24.04 dan sudah dihapus dari Debian 13):

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

Bagian yang sering keliru adalah: instal CLI sebagai user agent Anda, jangan pernah menggunakan sudo npm -g. Global prefix milik root akan menyebabkan error permission di kemudian hari dan meninggalkan file milik root di cache npm. Arahkan prefix npm ke home user 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 --version

Tambahkan export tersebut ke ~/.bashrc, di atas guard "If not running interactively, don't do anything" di dekat bagian awal file. Pane tmux default menjalankan login shell: tmux membiarkan default-command kosong, yang memberi tahu shell untuk menjalankan default-shell sebagai login shell, dan bash login membaca ~/.profile, yang pada Ubuntu mengambil sumber ~/.bashrc. Shell non-login, misalnya jika Anda mengatur default-command sendiri, membaca ~/.bashrc secara langsung. Jadi, ~/.bashrc mencakup kedua kasus, sedangkan ~/.profile saja akan melewatkan kasus kedua. Ada satu hal yang perlu diperhatikan: bash melewati ~/.profile jika ~/.bash_profile atau ~/.bash_login ada. Jadi, jika Anda membuat salah satunya, pastikan file tersebut juga mengambil sumber ~/.bashrc. Node per-user melalui version manager seperti nvm mencapai hasil yang sama; apa pun metodenya, tujuannya adalah agar npm install -g tidak pernah memerlukan sudo. npm tetap berfungsi dengan baik, tetapi dokumentasi Anthropic menandai skrip instalasi native sebagai rute yang direkomendasikan. Repo apt, dnf, dan apk bertanda tangan juga merupakan pilihan lain. Periksa dokumentasi instalasi Anthropic sebelum menempelkan perintah, karena metode instalasi dapat berubah.

Jalankan claude di dalam repo untuk memulainya. Pada eksekusi pertama, Anda akan diarahkan melalui proses autentikasi. Server headless tidak memiliki browser, sehingga proses tersebut memberikan URL untuk dibuka pada mesin Anda sendiri dan kode untuk dimasukkan kembali ke terminal. (API key di environment merupakan pilihan lainnya.) Apa pun metodenya, kredensial tersebut kini tersimpan di server. Selanjutnya, kita membahas bagian yang sering dilewati.

Membahas radius dampak

Agent dengan akses shell adalah shell. Agent tersebut dapat membaca semua hal yang dapat dibaca oleh user yang menjalankannya, serta melakukan push ke lokasi mana pun yang dapat digunakan user tersebut untuk melakukan push. Ini bukan kritik terhadap tool tersebut. Ini adalah definisinya. Karena itu, akun yang digunakan untuk menjalankannya lebih penting daripada pengaturan individual mana pun.

  • User khusus tanpa hak istimewa. Jangan masukkan user tersebut ke grup sudo, dan jangan gunakan home directory yang dibagikan dengan akun Anda sendiri.
  • Jangan menyimpan kredensial produksi di server. Jangan simpan ~/.aws/credentials yang berisi production key, jangan menyalin .env dari production, dan jangan gunakan password database yang memiliki akses tulis ke data penting. Berikan kredensial staging atau read-only kepada agent.
  • Token dengan cakupan terbatas. Gunakan GitHub token fine-grained yang dibatasi ke satu repository, atau deploy key jika akses baca sudah memadai.

Claude Code menyediakan flag --dangerously-skip-permissions yang melewati hampir semua permission prompt. Pada laptop atau project sementara, keputusan tersebut ada di tangan Anda. Pada server yang menyimpan token, flag ini menghilangkan penghalang terakhir antara instruksi yang salah ditafsirkan dan git push --force. Permission prompt yang dilewati juga tidak bersifat sepenuhnya aktif atau nonaktif. Dengan auto mode kini menjadi mode awal default (sejak Claude Code v2.1.283), Anda perlu mengetahui permission mode yang harus ditetapkan pada server yang tidak sedang Anda pantau. Penjelasan tentang perubahan yang sebenarnya dilakukan flag tersebut dan cara membatasi agent yang berjalan dengannya, mulai dari sandbox bawaan hingga VPS disposable, tersedia di menjalankan Claude Code dengan aman di server.

Deploy key vs SSH agent forwarding

Anda mungkin tergoda untuk ssh -A agar git dapat menggunakan key di laptop Anda. Pahami hak akses yang diberikan: agent forwarding mengekspos socket SSH agent lokal Anda kepada proses yang berjalan sebagai user tersebut di server. Semua proses yang berjalan sebagai agent, termasuk agent, dapat meminta key Anda untuk menandatangani permintaan ke host mana pun yang dapat dijangkaunya, selama Anda tetap terhubung. Hak akses ini jauh lebih luas daripada sekadar "membiarkan git melakukan pull pada satu repo ini".

Sebagai gantinya, buat key di server, daftarkan key tersebut sebagai deploy key untuk repository tertentu (berikan akses tulis hanya jika agent perlu melakukan push), lalu tetapkan identitas git agar commit dari server 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 tmux (sudo apt install tmux), lalu buat konfigurasi minimal ~/.tmux.conf:

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 claude

tmux new -A -s claude adalah perintah yang perlu diingat. Perintah ini menyambungkan ke sesi jika sesi tersebut sudah ada dan membuat sesi baru jika belum ada. Dengan demikian, satu perintah dapat digunakan untuk memulai pekerjaan dan melanjutkannya setelah koneksi terputus. Buat alias untuk perintah ini. Di dalam sesi, Ctrl-b c membuka window, Ctrl-b n dan Ctrl-b p berpindah antar-window, sedangkan Ctrl-b [ masuk ke mode salin untuk menggulir ke bagian sebelumnya (q untuk keluar).

Satu hal yang perlu diketahui tentang sesi yang tidak pernah Anda hentikan: agent mengirim ulang seluruh percakapan pada setiap giliran. Karena itu, baca apa saja yang menghabiskan token dalam sesi Claude Code yang berjalan lama sebelum membiarkan sesi tetap berjalan selama seminggu.

Mode kegagalan

"Sesi saya hilang." tmux ls menampilkan no server running on /tmp/tmux-1000/default. Ini hampir selalu berarti proses tidak pernah berjalan di dalam tmux. Anda masuk melalui SSH, menjalankan claude secara langsung, lalu koneksi terputus dan menghentikan proses tersebut. Tidak ada yang dapat dipulihkan. Kebiasaan yang mencegahnya: tmux new -A -s <project> adalah perintah pertama setiap kali login.

Pane mengecil menjadi kotak kecil. Opsi window-size menentukan client terlampir yang menetapkan ukuran. Sejak tmux 3.1 (Ubuntu 24.04 menyertakan 3.4), nilai defaultnya adalah latest, yaitu client dengan aktivitas terbaru. Namun, konfigurasi yang memuat window-size smallest akan menggunakan ukuran client terlampir yang paling kecil. Akibatnya, client lama yang masih terlampir dari mesin lain dapat menyempitkan tampilan. Paksa client lain terlepas saat Anda melakukan attach: tmux attach -d -t claude.

Build menampilkan Killed. Hanya satu kata tanpa stack trace. Konfirmasikan dengan sudo dmesg -T | grep -i -E 'out of memory|killed process'; kernel OOM killer memilih proses terbesar. Dari Node, Anda mungkin melihat FATAL ERROR: Ineffective mark-compacts near heap limit Allocation failed - JavaScript heap out of memory. Solusinya, secara berurutan: tambahkan swap (seperti di atas), batasi paralelisme test dan compiler, naikkan heap Node dengan NODE_OPTIONS=--max-old-space-size=..., atau gunakan VPS dengan spesifikasi lebih tinggi. OOM killer juga dapat memilih tmux server, bukan proses build, sehingga sesi Anda ikut terhenti. Jika systemd-oomd sedang berjalan, proses tersebut dapat menghentikan seluruh user slice dengan efek yang sama.

npm error code EACCES / permission denied, mkdir '/usr/lib/node_modules/...'. Instalasi global ke prefix milik root. Gunakan prefix ~/.npm-global di atas. Jika sebelumnya Anda pernah menjalankan sudo npm, 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 kadang-kadang. Export PATH Anda berada di ~/.bashrc, di bawah guard "If not running interactively, don't do anything", sehingga shell non-interaktif melewatinya. Pindahkan export tersebut ke atas guard itu dan tetap simpan di ~/.bashrc. Pane tmux default adalah login shell yang mencapai ~/.bashrc melalui ~/.profile milik Ubuntu, sedangkan shell non-login membaca ~/.bashrc secara langsung. Dengan demikian, satu file tersebut mencakup keduanya.

Warna menjadi kacau setelah attach. Penyebabnya adalah ketidakcocokan TERM. Baris default-terminal di atas adalah solusinya.

Sesi hilang setelah reboot. Ini bukan bug. tmux server adalah sebuah proses, dan reboot menghentikannya. Periksa uptime.

Apa yang mulai bermasalah seiring pertumbuhan

Lebih banyak proyek. Gunakan satu sesi tmux untuk setiap repo dan beri nama sesuai repo tersebut; tmux ls kemudian menjadi dasbor Anda. Jika disiplin penamaan diabaikan, Anda akan mendapatkan sesi 0, 1, 2. Setelah beberapa sesi berjalan secara bersamaan, sesi-sesi tersebut tidak harus bekerja secara terisolasi, karena satu sesi dapat mengirim pesan ke sesi lain pada mesin yang sama. Ini berguna ketika agent yang sedang melakukan refactor panjang ingin meminta agent lain menjalankan pengujian. Port juga dapat bertambah tanpa terkendali. Jika enam repo sama-sama menginginkan :3000, hentikan penetapan port secara manual dan biarkan reverse proxy Traefik merutekan beberapa aplikasi di bawah Docker Compose berdasarkan hostname.

Lebih banyak pengguna. Socket tmux berlaku per pengguna. Karena itu, dua developer pada mesin yang sama masing-masing memiliki server tmux sendiri dan tidak dapat melihat sesi pengguna lain. Berbagi satu sesi melalui socket bersama berarti semua orang mengetik ke shell yang sama sebagai pengguna Unix yang sama, beserta konsekuensi audit dan izin yang ditimbulkannya. Memisahkan pengguna adalah pilihan yang sederhana dan benar.

Pekerjaan tanpa pengawasan. tmux ditujukan untuk sesi interaktif yang Anda lampirkan. Pekerjaan terjadwal yang berjalan tanpa pengawasan sebaiknya ditempatkan dalam unit dan timer systemd. Dengan begitu, pekerjaan tersebut memperoleh logging, kebijakan restart, dan kemampuan bertahan setelah boot secara langsung. Menggunakan tmux untuk menjalankan pekerjaan seperti cron menunjukkan bahwa pekerjaan tersebut seharusnya dijalankan sebagai service.

Satu catatan terakhir: bind dev server yang dijalankan agent ke 127.0.0.1, bukan 0.0.0.0, lalu akses melalui tunnel SSH (ssh -L 3000:127.0.0.1:3000 agent@your-server), bukan dengan membuka port di ufw. Jika Anda sudah meneruskan sekitar enam port, atau ponsel dan laptop sama-sama perlu mengakses preview yang sama, tempatkan VPN WireGuard yang di-host sendiri pada VPS di depannya. Dengan demikian, dev server melakukan bind ke interface privat, dan ufw tetap menolak semua koneksi dari interface publik. Firewall hanya membantu jika Anda berhenti membuat lubang di dalamnya.

Claude Code bukan satu-satunya pilihan: menjalankan agent AI pemrograman pada VPS juga mencakup Aider dan Goose.

FAQ

Apakah Claude Code tetap berjalan setelah koneksi SSH terputus?

Hanya jika Anda menjalankannya di dalam tmux. Proses yang langsung dijalankan dari shell SSH merupakan child dari shell tersebut dan berhenti bersama pty ketika koneksi terputus. Di dalam tmux, shell menjadi milik server tmux yang detached, sehingga agent tetap bekerja di tengah tugas dan tmux attach mengembalikan Anda ke scrollback yang sama. Jadikan tmux new -A -s <project> perintah pertama setiap kali login agar masalah ini tidak terjadi lagi.

Apakah CLI sebaiknya saya instal dengan sudo npm install -g?

Tidak. Prefix global yang dimiliki root akan menyebabkan error EACCES pada instalasi berikutnya dan membuat file di npm cache dimiliki root. Atur prefix npm ke ~/.npm-global (atau gunakan version manager seperti nvm), lakukan instalasi sebagai user non-privileged agent, lalu export ~/.npm-global/bin ke PATH dari ~/.bashrc, di atas guard interaktif. Jika Anda sudah menjalankan sudo npm, perbaiki cache dengan sudo chown -R $(id -u):$(id -g) ~/.npm.

Apakah penerusan agent ssh -A aman pada server yang menjalankan agent?

Penerusan tersebut memberikan akses yang jauh lebih besar daripada yang diperlukan oleh tugas. Forwarding mengekspos socket SSH agent lokal Anda ke setiap proses yang berjalan sebagai user tersebut. Artinya, apa pun yang berjalan di server dapat meminta key Anda menandatangani koneksi ke host mana pun yang dapat dijangkaunya selama koneksi Anda tetap terpasang. Buat key ed25519 di server dan daftarkan sebagai deploy key per repository. Berikan akses tulis hanya jika agent memang harus melakukan push.

Mengapa build saya hanya menampilkan Killed?

Satu kata tanpa stack trace menunjukkan OOM killer pada kernel. 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. Terapkan perbaikan secara berurutan: tambahkan swapfile, batasi paralelisme pengujian dan compiler, naikkan NODE_OPTIONS=--max-old-space-size=..., lalu tingkatkan ukuran VPS. Perhatikan bahwa OOM killer dapat memilih server tmux, bukan proses build, sehingga seluruh sesi Anda ikut terputus.

tmux atau service systemd?

tmux cocok untuk sesi interaktif yang Anda sambungkan, pantau, dan gunakan untuk mengetik. Ini tepat untuk sesi agent. Pekerjaan terjadwal yang tidak dipantau siapa pun sebaiknya menggunakan unit dan timer systemd. Logging, kebijakan restart, dan kelangsungan saat boot tersedia secara bawaan. Jika Anda menggunakan tmux untuk menjalankan pekerjaan seperti cron, pekerjaan tersebut seharusnya menjadi service.