Claude Code draaien op een VPS met tmux
Voorkom dat uw Claude Code sessies stoppen na een SSH-disconnectie. Gebruik tmux op uw Linux VPS om agent-processen stabiel te laten draaien zonder SIGHUP.
Het probleem is het laptopdeksel, niet de CLI
Claude Code werkt goed op uw laptop totdat u deze dichtklapt: de SSH-sessie wordt verbroken, de shell ontvangt een SIGHUP, en de agent stopt drie minuten na het starten van een testrun. Gebruik de CLI op een machine die nooit in de slaapstand gaat. Gebruik een terminal multiplexer waarbij de processen geen kinderen zijn van uw SSH-sessie. Dat is de kernoplossing — tmux is het cruciale onderdeel, niet de installatie.
Deze pagina gaat over het beheren van een machine waarop u agents laat draaien. Als u geen Linux-server heeft die u aan kunt laten staan, is deze informatie niet van toepassing. Dat is de enige vereiste.
Wat tmux precies doet
Wanneer u via SSH verbinding maakt, voert sshd een shell uit via een fork en koppelt deze aan een pseudo-terminal; alles wat u vanuit die shell start, is een child-proces daarvan. Als u de verbinding verbreekt, sluit de kernel de pty, ontvangt de shell een SIGHUP, en wordt de verbinding met de child-processen verbroken. Processen die op de voorgrond draaien en langdurig zijn, stoppen dan.
tmux draait dit proces om. Het tmux commando dat u typt, is een thin client die via een unix socket communiceert met een tmux server die los van uw terminal draait. Shells binnen een sessie zijn child-processen van die server, niet van sshd. Als u de SSH-verbinding verbreekt, verdwijnt de client, maar de server, de sessie en de agent blijven tijdens de taak doorgaan. Verbind opnieuw verbinding via tmux attach en u bent terug in dezelfde shell met dezelfde scrollback. nohup overleeft een verbindingsoverdracht ook, maar biedt geen manier om terug te keren — u kunt niet opnieuw verbinden met een TUI die op de achtergrond draait. Claude Code is interactief; tmux (of screen) is hiervoor het juiste hulpmiddel.
De omvang van de machine bepalen
De CLI is een Node-proces; dit is niet wat de machine belast. De belasting komt van de processen die de agent voor u uitvoert: een build, een volledige testsuite, tsc, een language server, of een database in Docker. Plan de capaciteit op basis van de toolchain, niet op basis van de CLI. Voeg swap toe, zelfs als u dit nooit gaat gebruiken — dit voorkomt een harde OOM kill en maakt een trage build mogelijk:
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/fstabLet ook op de schijfruimte: repos, node_modules en Docker-images nemen snel toe. En als de toolchain verder gaat dan containers naar volledige virtuele machines — een KVM-gast, een lokale Kubernetes-node — controleer dan of het abonnement CPU-virtualisatie-extensies ondersteunt voordat u een keuze maakt. het uitvoeren van nested virtualization op een VPS is namelijk iets wat de provider voor u inschakelt, in plaats van iets wat u zelf vanuit de gastomgeving kunt activeren.
Een gebruiker zonder root-rechten eerst
Maak een specifieke gebruiker aan met een eigen home-directory en plaats uw public key op de juiste plek:
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_keysOpzettelijk maakt agent geen deel uit van de sudo groep. Als een systeempakket nodig is, installeert u dit handmatig. Deze beslissing voorkomt dat een foutieve shell-opdracht de host beschadigt.
SSH-hygiëne voor een server die continu draait
Wachtwoordauthenticatie op een machine die de hele dag op het publieke internet staat, een agent bevat en uw broncode host, vormt een onnodig risico. Schakel dit uit. Op Ubuntu 24.04 en Debian 13 bevat /etc/ssh/sshd_config /etc/ssh/sshd_config.d/*.conf, dus voeg een bestand toe in plaats van de hoofdconfiguratie te bewerken:
# /etc/ssh/sshd_config.d/10-hardening.conf
PasswordAuthentication no
KbdInteractiveAuthentication no
PermitRootLogin noValideer en herlaad — houd uw huidige sessie open terwijl u een nieuwe sessie test via een tweede terminal:
sudo sshd -t && sudo systemctl restart sshEen detail voor Ubuntu 24.04: sshd is socket-geactiveerd. Authenticatie-instellingen gelden op systemctl restart ssh, maar een wijziging in de luisterende Port vereist ook systemctl daemon-reload en een herstart van ssh.socket.
Gebruik daarna de firewall. Sta SSH toe voordat u het inschakelt, anders sluit u uzelf uit:
sudo ufw allow OpenSSH
sudo ufw default deny incoming
sudo ufw default allow outgoing
sudo ufw enableInstalleer fail2ban met een duidelijk beeld van de voordelen: zodra wachtwoordauthenticatie is uitgeschakeld, kan een brute-force aanval toch niet slagen — het houdt mislukte pogingen uit uw journal.
# /etc/fail2ban/jail.local
[sshd]
enabled = true
backend = systemd
maxretry = 5
bantime = 1hGebruik tot slot sudo apt install unattended-upgrades en sudo dpkg-reconfigure -plow unattended-upgrades voor automatische updates. Let op de interactie met tmux: als Unattended-Upgrade::Automatic-Reboot is ingeschakeld en een kernel-update voert een reboot uit, wordt de server herstart en wordt elke sessie beëindigd. Laat dit uitgeschakeld staan en herstart handmatig wanneer er geen processen actief zijn.
Installeer Node.js en Claude Code op Ubuntu
Claude Code is een Node CLI, dus u heeft een recente versie van Node nodig. Het pakket in de distributie is vaak verouderd; NodeSource is de standaardmethode op Ubuntu en Debian, en zij leveren een ondertekende repo (geen apt-key — die tool bestaat niet meer):
curl -fsSL https://deb.nodesource.com/setup_24.x | sudo -E bash -
sudo apt install -y nodejs
node --versionDe veelgemaakte fout: installeer de CLI als uw agent gebruiker, nooit met sudo npm -g. Een globale prefix die eigendom is van root veroorzaakt later permissiefouten en laat bestanden achter in de npm cache die eigendom zijn van root. Stel de prefix van npm eerst in op de home-directory van de gebruiker:
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 --versionDe export hoort in ~/.bashrc te staan, niet in ~/.profile. Plaats deze boven de "If not running interactively, don't do anything" guard bovenaan het bestand: tmux kan shells starten die geen login-shells zijn, welke ~/.bashrc lezen en ~/.profile overslaan — ~/.profile wordt alleen uitgevoerd voor login-shells. Een Node-installatie per gebruiker via een version manager zoals nvm bereikt hetzelfde resultaat; het doel is in beide gevallen dat npm install -g nooit sudo nodig heeft. npm werkt nog steeds correct, of gebruik het native install script van Anthropic, wat momenteel de gedocumenteerde standaard is. Controleer de installatie-documentatie van Anthropic voordat u commando's kopieert — installatiemethoden veranderen.
Voer claude uit binnen een repo om de tool te starten. De eerste keer wordt u door het authenticatieproces geleid; een headless server heeft geen browser, dus de flow geeft u een URL die u op uw eigen machine moet openen en een code die u moet invoeren in de terminal. (Een API-key in de environment is een alternatieve methode.) In beide gevallen staat de credential nu op de server — wat ons brengt bij het onderdeel dat mensen vaak overslaan.
Het blast radius-gesprek
Een agent met shell-toegang is een shell. Deze kan alles lezen wat de gebruiker waarvoor de agent draait kan lezen, en overal pushen waar die gebruiker kan pushen. Dit is geen kritiek op de tool, maar de definitie ervan — en dit is de reden waarom het account waaronder de agent draait belangrijker is dan elke individuele instelling.
- Dedicated, onbevoegde gebruiker. Geen
sudogroep, geen home directory die gedeeld wordt met uw eigen account. - Geen productie-credentials op de machine. Geen
~/.aws/credentialsmet productie-keys, geen.envgekopieerd vanuit productie, geen database-wachtwoord met schrijfrechten voor kritieke systemen. Geef de agent een staging- of read-only credential. - Scoped tokens. Een fine-grained GitHub token beperkt tot één repository; een deploy key wanneer read-only toegang voldoende is.
Claude Code bevat een flag die alle toestemmingsprompts volledig overslaat. Op een laptop, voor een tijdelijk project, is dat uw eigen keuze. Op een server met tokens verwijdert dit de laatste barrière tussen een verkeerd begrepen instructie en een git push --force. Wat de flag precies verandert, en hoe u een agent die hiermee draait beperkt — van de ingebouwde sandbox tot een disposable VPS — wordt behandeld in het veilig uitvoeren van Claude Code op een server.
Deploy key vs SSH agent forwarding
Het is verleidelijk om ssh -A te gebruiken zodat git de key op uw laptop kan gebruiken. Begrijp wat dit toestaat: agent forwarding maakt de socket van uw lokale SSH-agent toegankelijk voor processen die als die gebruiker op de machine draaien. Alles wat draait als agent — inclusief de agent — kan uw key vragen om te ondertekenen voor elke host die bereikbaar is, zolang u verbonden blijft. Dat is veel meer dan "laat git deze ene repo pullen".
Genereer in plaats daarvan een key op de server, registreer deze als een per-repository deploy key (alleen schrijfrechten als de agent moet pushen), en stel een git-identiteit in zodat commits vanaf de machine herkenbaar zijn:
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"De tmux workflow
Installeer het (sudo apt install tmux), en gebruik vervolgens een minimale ~/.tmux.conf:
set -g mouse on
set -g history-limit 50000
set -g default-terminal "tmux-256color"Vier commando's zijn voldoende voor dagelijks gebruik:
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 is het belangrijkste commando om te onthouden — het koppelt de sessie als deze bestaat en maakt een nieuwe sessie aan als deze niet bestaat. Eén commando dekt dus zowel het starten van de dag als het hervatten na een onderbreking. Maak hier een alias van. Binnen een sessie opent Ctrl-b c een window, waarmee u met Ctrl-b n en Ctrl-b p kunt wisselen, en met Ctrl-b [ gaat u naar de copy mode om terug te scrollen (q sluit deze modus).
Belangrijk om te weten over sessies die u nooit beëindigt: de agent stuurt bij elke beurt de volledige conversatie opnieuw. Lees daarom waar een langdurige Claude Code sessie tokens aan verbruikt voordat u een sessie een week laat draaien.
Failure modes
"My session is gone." tmux ls geeft no server running on /tmp/tmux-1000/default weer. Dit betekent bijna altijd dat het proces nooit in tmux draaide — u bent ingelogd via SSH, heeft claude direct uitgevoerd, en de verbindingsoverbreking heeft het proces beëindigd. Er is niets te herstellen. De methode om dit te voorkomen: tmux new -A -s <project> is het eerste commando na elke login.
Het paneel krimpt tot een klein vak. tmux past de sessiegrootte aan naar de kleinste verbonden client. Een verouderde client die nog verbonden is vanaf een andere machine verkleint het scherm. Forceer de verbindingen van anderen bij het verbinden: tmux attach -d -t claude.
Een build geeft Killed weer. Eén woord, zonder stack trace. Controleer dit met sudo dmesg -T | grep -i -E 'out of memory|killed process' — de kernel OOM killer heeft het grootste proces geselecteerd. Bij Node ziet u mogelijk in plaats daarvan FATAL ERROR: Ineffective mark-compacts near heap limit Allocation failed - JavaScript heap out of memory. Oplossingen, in volgorde: voeg swap toe (hierboven), beperk de test- en compiler-parallelle processen, verhoog de heap van Node met NODE_OPTIONS=--max-old-space-size=..., of vergroot de VPS. De OOM killer kan ook de tmux server selecteren in plaats van de build, waardoor uw sessie verloren gaat; als systemd-oomd draait, kan het een volledige user slice met hetzelfde effect beëindigen.
npm error code EACCES / permission denied, mkdir '/usr/lib/node_modules/...'. Een globale installatie in een prefix die eigendom is van root. Gebruik de ~/.npm-global prefix hierboven. Als u op een bepaald moment al sudo npm heeft uitgevoerd, ziet u mogelijk ook Your cache folder contains root-owned files — herstel dit met sudo chown -R $(id -u):$(id -g) ~/.npm.
claude: command not found — maar alleen soms. Uw PATH export staat in ~/.bashrc onder de "If not running interactively, don't do anything" guard, waardoor non-interactieve shells deze overslaan. Verplaats de export naar boven die guard en bewaar deze in ~/.bashrc, niet in ~/.profile: tmux kan non-login shells starten, die ~/.bashrc lezen en ~/.profile nooit aanraken.
Verstoorde kleuren na het verbinden. Een TERM mismatch — de default-terminal regel hierboven is de oplossing.
Sessies verdwijnen na een reboot. Geen bug: de tmux server is een proces, en een reboot beëindigt dit proces. Controleer uptime.
Wat misgaat bij schaalvergroting
Meer projecten. Gebruik één tmux-sessie per repo, vernoemd naar de repo; tmux ls is dan uw dashboard. Zonder strikte naamgeving krijgt u sessies 0, 1 en 2. Poorten verspreiden zich op dezelfde manier — als zes repo's allemaal :3000 vereisen, is het tijd om te stoppen met handmatige toewijzing. Laat een Traefik reverse proxy meerdere apps onder Docker Compose het werk doen via hostnames.
Meer gebruikers. tmux-sockets zijn gebruikersgebonden. Twee ontwikkelaars op dezelfde machine hebben elk een eigen tmux-server en kunnen elkaars sessies niet zien. Het delen van één sessie via een gedeelde socket betekent dat iedereen in dezelfde shell typt als dezelfde Unix-gebruiker. Dit heeft gevolgen voor de audit-logs en permissies. Gebruikers scheiden is de eenvoudige, correcte oplossing.
Onbewaakt werk. tmux is bedoeld voor interactieve sessies waar u zich aan koppelt. Taken die volgens een schema draaien zonder toezicht horen in een systemd unit en timer. Daar krijgt u automatisch logging, een restart-policy en overleving na een reboot. Het gebruik van tmux voor een cron-achtige taak is een teken dat de taak een service moet worden.
Nog een laatste opmerking: bind dev-servers zodat de agent start op 127.0.0.1 in plaats van 0.0.0.0, en bereik deze via een SSH-tunnel (ssh -L 3000:127.0.0.1:3000 agent@your-server in plaats van poorten openen in ufw. Wanneer u een half dozijn poorten moet doorsturen, of wanneer een telefoon en een laptop beide dezelfde preview willen bekijken, gebruik dan een self-hosted WireGuard VPN op de VPS. De dev-servers binden aan een private interface en ufw blokkeert alles van de publieke interface. Een firewall is alleen nuttig als u stopt met het maken van gaten in de configuratie.
Claude Code is niet de enige keuze: het draaien van een coding AI agent op een VPS weegt ook Aider en Goose af.
FAQ
Blijft Claude Code doorlopen nadat mijn SSH-verbinding is verbroken?
Alleen als u het binnen tmux heeft gestart. Een proces dat direct vanuit de SSH-shell is gestart, is een child van die shell en stopt zodra de pty wegvalt. Binnen tmux behoort de shell tot de detached tmux server, waardoor de agent blijft werken tijdens de taak en tmux attach u terugbrengt naar dezelfde scrollback. Gebruik tmux new -A -s <project> als het eerste commando na elke login om dit probleem te voorkomen.
Moet ik de CLI installeren met sudo npm install -g?
Nee. Een globale prefix met root-rechten veroorzaakt EACCES fouten bij latere installaties en root-rechten voor bestanden in de npm cache. Stel de prefix van npm in op ~/.npm-global (of gebruik een version manager zoals nvm), installeer als de onbevoegde agent user, en exporteer ~/.npm-global/bin naar PATH vanaf ~/.bashrc, boven de interactive guard. Als u sudo npm al een keer heeft uitgevoerd, herstel dan de cache met sudo chown -R $(id -u):$(id -g) ~/.npm.
Is ssh -A agent forwarding veilig op een machine waar een agent draait?
Dit geeft veel meer rechten dan de taak nodig heeft. Forwarding maakt de socket van uw lokale SSH agent toegankelijk voor elk proces dat als die user draait. Hierdoor kan alles op de machine uw key gebruiken om te signeren voor elke host die bereikbaar is, zolang u verbonden blijft. Genereer een ed25519 key op de server en registreer deze als een per-repository deploy key, met alleen write access als de agent daadwerkelijk moet pushen.
Waarom print mijn build alleen Killed?
Een enkele regel zonder stack trace duidt op de kernel OOM killer. Controleer dit met sudo dmesg -T | grep -i -E 'out of memory|killed process'; vanuit Node ziet u mogelijk JavaScript heap out of memory in plaats daarvan. Voer de oplossingen in deze volgorde uit: voeg een swapfile toe, beperk de test- en compiler parallelism, verhoog NODE_OPTIONS=--max-old-space-size=..., en schaal de VPS op. Let op dat de OOM killer de tmux server kan kiezen in plaats van de build, waardoor uw hele sessie verloren gaat.
tmux of een systemd service?
tmux is geschikt voor interactieve sessies die u koppelt, monitort en waarin u typt; dit is precies wat een agent sessie is. Werk dat volgens een schema draait zonder toezicht hoort in een systemd unit en timer, waar logging, een restart policy en boot survival standaard aanwezig zijn. Als u tmux gebruikt voor een taak die als een cron-job werkt, dan moet de taak een service worden.