Claude Code auf einem VPS mit tmux ausführen
So überlebt Claude Code getrennte SSH-Verbindungen: Installation auf einem Linux-VPS, tmux-Grundlagen, Absicherung und typische Fehler wie SIGHUP.
Das Problem ist der Laptopdeckel, nicht die CLI
Claude Code läuft auf Ihrem Laptop problemlos, bis Sie ihn schließen: Die SSH-Sitzung wird beendet, die Shell empfängt ein SIGHUP, und der Agent beendet sich zusammen mit ihr drei Minuten nach Beginn eines Testlaufs. Führen Sie die CLI auf einem Rechner aus, der nicht in den Ruhezustand wechselt, und innerhalb eines Terminal-Multiplexers, dessen Prozesse keine Kindprozesse Ihrer SSH-Sitzung sind. Das ist der gesamte Kniff. Der entscheidende Bestandteil ist tmux, nicht die Installation.
Auf dieser Seite geht es um den Betrieb eines Rechners, auf dem Sie Agents laufen lassen. Wenn Sie keinen Linux-Server haben, den Sie eingeschaltet lassen können, trifft nichts davon auf Sie zu. Das ist die einzige ehrliche Voraussetzung.
Was tmux tatsächlich macht
Bei einer SSH-Verbindung startet sshd eine Shell und stellt ihr ein Pseudo-Terminal bereit. Alles, was Sie in dieser Shell starten, ist ein untergeordneter Prozess dieser Shell. Wird die Verbindung getrennt, gibt der Kernel das pty frei. Die Shell erhält SIGHUP und gibt das Signal wiederum an ihre untergeordneten Prozesse weiter. Lang laufende Prozesse im Vordergrund werden beendet.
tmux kehrt diese Besitzverhältnisse um. Der von Ihnen eingegebene Befehl tmux ist ein schlanker Client, der über einen Unix-Socket mit einem tmux server kommuniziert. Dieser läuft unabhängig von Ihrem Terminal. Die Shells innerhalb einer Sitzung sind untergeordnete Prozesse dieses Servers und nicht von sshd. Wird die SSH-Verbindung beendet, endet der Client. Der Server, die Sitzung und der gerade laufende Agent bleiben jedoch aktiv. Stellen Sie die Verbindung wieder her, führen Sie tmux attach aus und befinden Sie sich wieder in derselben Shell mit demselben Scrollback. Auch nohup übersteht ein SIGHUP, bietet Ihnen aber keine Möglichkeit zur Rückkehr. An eine TUI, die im Hintergrund läuft, können Sie sich nicht erneut anhängen. Claude Code ist interaktiv. Dafür ist tmux (oder screen) das richtige Werkzeug.
Die Maschine dimensionieren
Die CLI ist ein Node-Prozess. Sie ist nicht der Grund, warum die Maschine voll ausgelastet wird. Dafür ist alles verantwortlich, was der Agent in Ihrem Auftrag ausführt: ein Build, eine vollständige Testsuite, tsc, ein Sprachserver oder eine Datenbank in Docker. Dimensionieren Sie die Maschine für die Toolchain, nicht für die CLI. Richten Sie auch dann Swap ein, wenn Sie ihn voraussichtlich nie benötigen. Dadurch wird ein harter OOM-Kill zu einem langsamen 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/fstabBehalten Sie auch den Speicherplatz im Blick: Repositories, node_modules und Docker-Images wachsen schnell. Wenn die Toolchain über Container hinaus vollständige virtuelle Maschinen verwendet, etwa einen KVM-Gast oder einen lokalen Kubernetes-Knoten, prüfen Sie vor der Buchung, ob der Tarif die CPU-Virtualisierungserweiterungen bereitstellt. Verschachtelte Virtualisierung auf einem VPS ausführen ist etwas, das der Anbieter für Sie aktiviert, und keine Funktion, die Sie innerhalb des Gasts selbst einschalten.
Zuerst ein Benutzer ohne root-Rechte
Erstellen Sie einen dedizierten Benutzer mit einem eigenen Home-Verzeichnis, und hinterlegen Sie seinen öffentlichen Schlüssel:
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_keysTesten Sie die Anmeldung von einem zweiten Terminal aus, bevor Sie fortfahren, solange die Kennwortauthentifizierung noch als Fallback verfügbar ist. Wenn dabei Zugriff verweigert (publickey) erscheint, liegt die Ursache normalerweise bei den Besitzrechten oder dem Modus des Verzeichnisses .ssh und nicht beim Schlüssel selbst.
Absichtlich gehört agent nicht zur Gruppe sudo. Wenn ein Systempaket benötigt wird, installieren Sie es. Diese eine Entscheidung beseitigt die meisten Möglichkeiten, wie ein versehentlich ausgeführter Shell-Befehl den Host beschädigen kann.
SSH-Hygiene für einen dauerhaft laufenden Server
Passwortauthentifizierung auf einem Rechner, der den ganzen Tag öffentlich im Internet erreichbar ist und einen Agent sowie Ihren Quellcode enthält, ist ein unnötiges Risiko. Deaktivieren Sie sie. Unter Ubuntu 24.04 und Debian 13 enthält /etc/ssh/sshd_config bereits /etc/ssh/sshd_config.d/*.conf. Legen Sie daher eine Datei ab, statt die Hauptkonfiguration zu bearbeiten:
# /etc/ssh/sshd_config.d/10-hardening.conf
PasswordAuthentication no
KbdInteractiveAuthentication no
PermitRootLogin noPrüfen Sie die Konfiguration und laden Sie sie neu. Lassen Sie Ihre aktuelle Sitzung geöffnet, während Sie eine neue Sitzung von einem zweiten Terminal aus testen:
sudo sshd -t && sudo systemctl restart sshEin Detail unter Ubuntu 24.04: sshd wird über einen Socket aktiviert. Die Authentifizierungseinstellungen gelten für systemctl restart ssh. Eine Änderung am abhörenden Port erfordert jedoch zusätzlich systemctl daemon-reload und einen Neustart von ssh.socket.
Dann kommt die Firewall. Erlauben Sie SSH, bevor Sie die Firewall aktivieren. Andernfalls sperren Sie sich selbst aus:
sudo ufw allow OpenSSH
sudo ufw default deny incoming
sudo ufw default allow outgoing
sudo ufw enableInstallieren Sie fail2ban mit einer klaren Vorstellung davon, welchen Nutzen es hat: Sobald die Passwortauthentifizierung deaktiviert ist, können Brute-Force-Angriffe ohnehin nicht erfolgreich sein. fail2ban hält fehlgeschlagene Versuche aus Ihrem Journal fern.
# /etc/fail2ban/jail.local
[sshd]
enabled = true
backend = systemd
maxretry = 5
bantime = 1hFühren Sie abschließend automatische Updates mit sudo apt install unattended-upgrades und sudo dpkg-reconfigure -plow unattended-upgrades durch. Beachten Sie die Wechselwirkung mit tmux: Aktivieren Sie Unattended-Upgrade::Automatic-Reboot, führt ein Kernel-Update zu einem Neustart des Servers und beendet damit jede Sitzung. Lassen Sie die Option deaktiviert und starten Sie den Server nach Ihrem eigenen Zeitplan neu, wenn keine Aufgaben ausgeführt werden. Die gleiche Vorsicht gilt für ein Release-Upgrade: das Upgrade des Servers von Ubuntu 24.04 auf 26.04 startet sshd und den Kernel neu. Es sollte daher in einem Wartungsfenster stattfinden, in dem keine tmux-Sitzung wichtige Arbeiten ausführt.
Node.js und Claude Code unter Ubuntu installieren
Claude Code ist eine Node-CLI. Sie benötigen daher eine aktuelle Node-Version. Das Distributionspaket ist häufig veraltet. NodeSource ist unter Ubuntu und Debian der übliche Weg und stellt ein signiertes Repository bereit. apt-key ist daher nicht erforderlich, da dieses Tool entfernt wurde:
curl -fsSL https://deb.nodesource.com/setup_24.x | sudo -E bash -
sudo apt install -y nodejs
node --versionNun zu dem Punkt, der häufig falsch umgesetzt wird: Installieren Sie die CLI als Benutzer agent, niemals mit sudo npm -g. Ein globales, root-eigenes Präfix führt später zu Berechtigungsfehlern und hinterlässt root-eigene Dateien im npm-Cache. Setzen Sie das Präfix von npm zunächst auf das Home-Verzeichnis des Benutzers:
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 --versionDer Export gehört in ~/.bashrc, nicht in ~/.profile. Er muss oberhalb der Guard-Zeile „If not running interactively, don't do anything“ am Anfang der Datei stehen. tmux kann nicht-interaktive Shells starten. Diese lesen ~/.bashrc und überspringen ~/.profile. ~/.profile wird nur für Login-Shells ausgeführt. Ein benutzerspezifisches Node über einen Versionsmanager wie nvm erreicht dasselbe. Das Ziel ist in jedem Fall, dass npm install -g sudo nie benötigt. npm funktioniert weiterhin problemlos. Alternativ können Sie das native Installationsskript von Anthropic verwenden. Dies ist derzeit die dokumentierte Standardmethode. Prüfen Sie vor dem Einfügen die Installationsdokumentation von Anthropic, da sich die Installationsmethoden ändern.
Führen Sie claude innerhalb eines Repositorys aus, um es zu starten. Beim ersten Start werden Sie durch die Authentifizierung geführt. Auf einem Headless-System ist kein Browser verfügbar. Daher erhalten Sie eine URL, die Sie auf Ihrem eigenen Rechner öffnen, sowie einen Code, den Sie anschließend im Terminal eingeben. Eine API-Key-Variable in der Umgebung ist die andere Möglichkeit. In beiden Fällen liegt dieses Zugangsmittel nun auf dem Server. Damit kommen wir zu dem Teil, den viele überspringen.
Die Diskussion über den Schadensradius
Ein Agent mit Shell-Zugriff ist eine Shell. Er kann alles lesen, worauf der Benutzer, unter dem er ausgeführt wird, zugreifen kann, und überallhin pushen, wohin dieser Benutzer pushen kann. Das ist keine Kritik am Tool, sondern seine Definition. Deshalb ist das verwendete Konto wichtiger als jede einzelne Einstellung.
- Dedizierter, unprivilegierter Benutzer. Keine
sudo-Gruppe und kein gemeinsam mit Ihrem eigenen Konto verwendetes Home-Verzeichnis. - Keine Produktionszugangsdaten auf dem Server. Keine
~/.aws/credentialsmit Produktionsschlüsseln, kein aus der Produktion kopiertes.envund kein Datenbankpasswort mit Schreibzugriff auf relevante Systeme. Geben Sie dem Agenten stattdessen eine Zugangsdaten für Staging oder nur für Lesezugriffe. - Eingeschränkte Tokens. Ein fein abgestufter GitHub-Token, der auf ein Repository beschränkt ist, oder ein Deploy Key, wenn Lesezugriff ausreicht.
Claude Code stellt ein Flag bereit, das seine Berechtigungsabfragen vollständig überspringt. Auf einem Laptop oder in einem Wegwerfprojekt liegt diese Entscheidung bei Ihnen. Auf einem Server mit Tokens entfernt es die letzte Schutzschicht zwischen einer falsch verstandenen Anweisung und einem git push --force. Die übersprungenen Abfragen sind außerdem nicht strikt auf alles oder nichts beschränkt. Da der Auto-Modus als neue Standardeinstellung eingeführt wird, sollten Sie wissen, welcher Berechtigungsmodus auf einem Server fest eingestellt werden sollte, den Sie nicht überwachen. Welche Änderung das Flag tatsächlich bewirkt und wie Sie einen damit ausgeführten Agenten einschließen, von der integrierten Sandbox bis zu einem Wegwerf-VPS, wird unter Claude Code sicher auf einem Server ausführen beschrieben.
Deploy Key oder SSH-Agent-Weiterleitung
Es ist verlockend, ssh -A zu verwenden, damit git den Schlüssel auf Ihrem Laptop nutzen kann. Machen Sie sich klar, welche Berechtigungen Sie damit vergeben: Die Agent-Weiterleitung stellt den Socket Ihres lokalen SSH-Agents Prozessen zur Verfügung, die auf dem Server unter diesem Benutzer ausgeführt werden. Alles, was als agent ausgeführt wird, einschließlich des Agents, kann Ihren Schlüssel auffordern, für jeden erreichbaren Host eine Signatur zu erstellen, solange Ihre Verbindung besteht. Das geht weit über „git soll dieses eine Repository pullen“ hinaus.
Erzeugen Sie stattdessen einen Schlüssel auf dem Server, registrieren Sie ihn als repositorybezogenen Deploy Key, und aktivieren Sie Schreibzugriff nur, wenn der Agent pushen muss. Legen Sie außerdem eine git-Identität fest, damit Commits vom Server erkennbar sind:
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"Der tmux-Workflow
Installieren Sie es (sudo apt install tmux) und erstellen Sie anschließend eine minimale ~/.tmux.conf:
set -g mouse on
set -g history-limit 50000
set -g default-terminal "tmux-256color"Vier Befehle decken die tägliche Nutzung ab:
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 ist der wichtigste Befehl. Er verbindet Sie mit der Sitzung, wenn sie bereits existiert, und erstellt sie andernfalls. Damit deckt ein einziger Befehl sowohl den Arbeitsbeginn als auch die Wiederaufnahme nach einer unterbrochenen Verbindung ab. Legen Sie dafür einen Alias an. Innerhalb einer Sitzung öffnet Ctrl-b c ein Fenster. Mit Ctrl-b n und Ctrl-b p wechseln Sie zwischen den Fenstern. Ctrl-b [ aktiviert den Kopiermodus, um im Verlauf zurückzuscrollen (mit q verlassen Sie ihn).
Bei Sitzungen, die Sie nie beenden, sollten Sie Folgendes beachten: Der Agent sendet bei jeder Eingabe die gesamte Unterhaltung erneut. Lesen Sie daher wofür eine langfristig laufende Claude-Code-Sitzung ihre Tokens verbraucht, bevor Sie eine Sitzung eine Woche lang laufen lassen.
Fehlerbilder
„Meine Sitzung ist verschwunden.“ tmux ls gibt no server running on /tmp/tmux-1000/default aus. Das bedeutet fast immer, dass der Prozess nie innerhalb von tmux lief, Sie sich per SSH verbunden, claude direkt ausgeführt haben und die Trennung den Prozess beendet hat. Es gibt nichts wiederherzustellen. Die Gewohnheit, die das verhindert: tmux new -A -s <project> ist nach jeder Anmeldung der erste Befehl.
Das Pane schrumpft zu einem winzigen Kasten. tmux richtet die Größe einer Sitzung nach dem kleinsten verbundenen Client. Ein noch verbundener, veralteter Client von einem anderen Rechner verkleinert daher die Anzeige. Erzwingen Sie beim Verbinden die Trennung der anderen Clients: tmux attach -d -t claude.
Ein Build gibt Killed aus. Nur ein Wort, kein Stacktrace. Prüfen Sie mit sudo dmesg -T | grep -i -E 'out of memory|killed process': Der OOM-Killer des Kernels hat den größten Prozess ausgewählt. Bei Node sehen Sie stattdessen möglicherweise FATAL ERROR: Ineffective mark-compacts near heap limit Allocation failed - JavaScript heap out of memory. Beheben Sie das Problem in dieser Reihenfolge: Fügen Sie Swap hinzu (siehe oben), begrenzen Sie die Parallelität von Tests und Compilern, erhöhen Sie den Heap von Node mit NODE_OPTIONS=--max-old-space-size=... oder wählen Sie einen größeren VPS. Der OOM-Killer kann auch den tmux-Server statt des Builds auswählen und dadurch Ihre Sitzung beenden. Wenn systemd-oomd ausgeführt wird, kann es einen vollständigen User-Slice mit demselben Ergebnis beenden.
npm error code EACCES / permission denied, mkdir '/usr/lib/node_modules/...'. Eine globale Installation in ein root-eigenes Präfix. Verwenden Sie das oben angegebene ~/.npm-global-Präfix. Wenn Sie zuvor bereits irgendwann sudo npm ausgeführt haben, sehen Sie möglicherweise auch Your cache folder contains root-owned files. Reparieren Sie dies mit sudo chown -R $(id -u):$(id -g) ~/.npm.
claude: command not found, aber nur manchmal. Ihr PATH-Export steht in ~/.bashrc unterhalb der Guard-Zeile „If not running interactively, don't do anything“. Nicht-interaktive Shells überspringen ihn daher. Verschieben Sie den Export oberhalb dieser Guard-Zeile und lassen Sie ihn in ~/.bashrc, nicht in ~/.profile: tmux kann nicht-Login-Shells starten. Diese lesen ~/.bashrc und greifen nie auf ~/.profile zu.
Verfälschte Farben nach dem Verbinden. Ein TERM-Mismatch. Die obige Zeile default-terminal behebt das Problem.
Sitzungen verschwinden nach einem Reboot. Das ist kein Fehler: Der tmux-Server ist ein Prozess, und ein Reboot beendet ihn. Prüfen Sie uptime.
Was bei zunehmender Nutzung problematisch wird
Mehr Projekte. Verwenden Sie pro Repository eine eigene tmux-Sitzung und benennen Sie sie nach dem Repository. tmux ls ist dann Ihre Übersicht. Wenn Sie diese Benennung nicht konsequent verwenden, entstehen Sitzungen wie 0, 1 und 2. Sobald mehrere Sitzungen gleichzeitig laufen, müssen sie nicht isoliert arbeiten, da eine Sitzung einer anderen auf demselben Host eine Nachricht senden kann. Das ist nützlich, wenn der Agent, der gerade ein lang laufendes Refactoring ausführt, einen zweiten Agenten mit dem Ausführen der Tests beauftragen soll. Bei Ports entsteht dasselbe Problem. Wenn sechs Repositories jeweils :3000 verwenden möchten, sollten Sie die Ports nicht mehr manuell zuweisen. Lassen Sie stattdessen einen Traefik-Reverse-Proxy mehrere Anwendungen unter Docker Compose anhand des Hostnamens weiterleiten.
Mehrere Benutzer. tmux-Sockets gelten pro Benutzer. Zwei Entwickler auf demselben Host erhalten daher jeweils ihren eigenen tmux-Server und können die Sitzungen des jeweils anderen nicht sehen. Wenn Sie eine Sitzung über einen gemeinsamen Socket freigeben, geben alle Benutzer Befehle in derselben Shell unter demselben Unix-Benutzer ein. Das hat entsprechende Auswirkungen auf Protokollierung und Berechtigungen. Getrennte Benutzerkonten sind die einfache und korrekte Lösung.
Unbeaufsichtigte Aufgaben. tmux ist für interaktive Sitzungen vorgesehen, an die Sie sich anhängen. Zeitgesteuerte Aufgaben, die niemand überwacht, gehören in eine systemd-Unit mit Timer. Dort erhalten sie Protokollierung, eine Neustartregel und überstehen Reboots standardmäßig. Wenn Sie tmux zum Ausführen einer cron-ähnlichen Aufgabe verwenden, ist das ein Hinweis darauf, dass die Aufgabe als Dienst ausgeführt werden sollte.
Noch ein Hinweis: Binden Sie von einem Agenten gestartete Entwicklungsserver an 127.0.0.1 statt an 0.0.0.0. Greifen Sie über einen SSH-Tunnel (ssh -L 3000:127.0.0.1:3000 agent@your-server) auf sie zu, anstatt Ports in ufw zu öffnen. Wenn Sie bereits ein halbes Dutzend Ports weiterleiten oder sowohl ein Smartphone als auch ein Laptop dieselbe Vorschauinstanz verwenden sollen, schalten Sie stattdessen ein selbst gehostetes WireGuard-VPN auf dem VPS davor. Die Entwicklungsserver binden dann an eine private Schnittstelle, und ufw verweigert weiterhin alle Verbindungen über die öffentliche Schnittstelle. Die Firewall hilft nur, wenn Sie nicht ständig neue Ausnahmen einrichten.
Claude Code ist nicht die einzige Option: Beim Ausführen eines Coding-KI-Agenten auf einem VPS können Sie auch Aider und Goose berücksichtigen.
FAQ
Läuft Claude Code weiter, wenn meine SSH-Verbindung abbricht?
Nur wenn Sie es innerhalb von tmux gestartet haben. Ein Prozess, den Sie direkt aus der SSH-Shell starten, ist ein Kindprozess dieser Shell und wird zusammen mit dem pty beendet, wenn die Verbindung abbricht. Innerhalb von tmux gehört die Shell zum getrennten tmux-Server. Der Agent arbeitet daher mitten in der Aufgabe weiter, und tmux attach bringt Sie zurück in denselben Scrollback. Machen Sie tmux new -A -s <project> nach jeder Anmeldung zum ersten Befehl. Dann tritt das Problem nicht mehr auf.
Sollte ich die CLI mit sudo npm install -g installieren?
Nein. Ein globales, root-eigenes Präfix führt bei späteren Installationen zu EACCES-Fehlern und zu root-eigenen Dateien im npm-Cache. Setzen Sie das Präfix von npm auf ~/.npm-global oder verwenden Sie einen Versionsmanager wie nvm. Installieren Sie als nicht privilegierter Benutzer agent und exportieren Sie ~/.npm-global/bin aus ~/.bashrc nach PATH, und zwar vor der interaktiven Abfrage. Wenn Sie sudo npm bereits einmal ausgeführt haben, reparieren Sie den Cache mit sudo chown -R $(id -u):$(id -g) ~/.npm.
Ist ssh -A Agent Forwarding auf einem Server sicher, auf dem ein Agent läuft?
Es gewährt deutlich mehr Rechte als für die Aufgabe erforderlich. Durch das Forwarding wird der Socket Ihres lokalen SSH-Agenten für jeden Prozess verfügbar, der unter diesem Benutzer läuft. Dadurch kann jeder Prozess auf dem Server Ihren Schlüssel auffordern, für jeden erreichbaren Host zu signieren, solange Ihre Verbindung besteht. Erzeugen Sie auf dem Server einen ed25519-Schlüssel und registrieren Sie ihn als Deploy Key für das jeweilige Repository. Schreibzugriff sollte er nur erhalten, wenn der Agent tatsächlich Push-Vorgänge ausführen muss.
Warum gibt mein Build nur Killed aus?
Ein einzelnes Wort ohne Stacktrace weist auf den OOM-Killer des Kernels hin. Bestätigen Sie dies mit sudo dmesg -T | grep -i -E 'out of memory|killed process'. Aus Node sehen Sie stattdessen möglicherweise JavaScript heap out of memory. Arbeiten Sie die Abhilfemaßnahmen in dieser Reihenfolge durch: Erstellen Sie eine Swap-Datei, begrenzen Sie die Parallelität von Tests und Compiler, erhöhen Sie NODE_OPTIONS=--max-old-space-size=... und wählen Sie anschließend einen größeren VPS. Beachten Sie, dass der OOM-Killer statt des Builds den tmux-Server beenden kann. Dadurch verlieren Sie die gesamte Sitzung.
tmux oder ein systemd-Dienst?
tmux eignet sich für interaktive Sitzungen, an die Sie sich verbinden, die Sie überwachen und in die Sie Eingaben machen. Genau das ist eine Agent-Sitzung. Arbeit, die nach einem Zeitplan ohne Überwachung ausgeführt wird, gehört in eine systemd-Unit mit Timer. Dort sind Protokollierung, eine Neustart-Richtlinie und das Überleben eines Reboots direkt verfügbar. Wenn Sie tmux für einen cron-ähnlichen Job verwenden möchten, sollte dieser Job als Dienst eingerichtet werden.