SSD Nodes Learn Hosting plans →
Guides Matt ConnorPar Matt Connor · Mis à jour le 2026-08-22

Exécuter Claude Code sur un VPS avec tmux

Faites tourner Claude Code sur un VPS Linux toujours allumé avec tmux : vos sessions survivent aux coupures SSH. Installation, durcissement et erreurs à prévoir.

Le problème vient du capot du portable, pas de la CLI

Claude Code fonctionne correctement sur votre portable jusqu’à ce que vous refermiez le capot : la session SSH se ferme, le shell reçoit un SIGHUP et l’agent s’arrête avec elle trois minutes après le début d’un test. Exécutez la CLI sur une machine qui ne se met jamais en veille, dans un multiplexeur de terminal dont les processus ne sont pas des enfants de votre session SSH. C’est tout le principe. La partie essentielle est tmux, pas l’installation.

Cette page explique comment exploiter une machine sur laquelle vous laissez des agents s’exécuter. Si vous n’avez pas de serveur Linux que vous pouvez laisser allumé, rien de tout cela ne s’applique. C’est le seul prérequis à prendre au sérieux.

Ce que fait réellement tmux

Lorsque vous vous connectez en SSH, sshd lance un shell et lui attribue un pseudo-terminal. Tout ce que vous lancez depuis ce shell en devient un processus fils. Si la connexion est interrompue, le noyau détruit le pty, le shell reçoit SIGHUP, puis transmet à son tour le signal de déconnexion à ses processus fils. Les processus de longue durée exécutés au premier plan s’arrêtent.

tmux inverse cette relation de parenté. La commande tmux que vous saisissez est un client léger qui communique via un socket Unix avec un serveur tmux exécuté indépendamment de votre terminal. Les shells d’une session sont les processus fils de ce serveur, et non de sshd. Si la connexion SSH est interrompue, le client disparaît, tandis que le serveur, la session et l’agent en cours d’exécution continuent de fonctionner. Reconnectez-vous, exécutez tmux attach, et vous retrouvez le même shell avec le même historique de défilement. nohup résiste également à une déconnexion, mais ne permet pas de reprendre la session : vous ne pouvez pas vous rattacher à une interface TUI exécutée en arrière-plan. Claude Code est interactif ; tmux (ou screen) est donc l’outil adapté.

Dimensionnement du serveur

La CLI est un processus Node ; ce n’est pas elle qui remplit la machine. Ce qui remplit la machine, ce sont les tâches que l’agent exécute pour vous : un build, une suite complète de tests, tsc, un language server ou une base de données dans Docker. Dimensionnez le serveur en fonction de la toolchain, pas de la CLI. Ajoutez du swap même si vous prévoyez de ne jamais l’utiliser : cela transforme un arrêt forcé pour OOM en build lent :

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

Surveillez aussi le disque : les dépôts, node_modules et les images Docker s’accumulent rapidement. Et si la toolchain utilise des machines virtuelles complètes en plus des conteneurs, par exemple un invité KVM ou un nœud Kubernetes local, vérifiez que l’offre expose les extensions de virtualisation du CPU avant de vous engager. En effet, exécuter de la virtualisation imbriquée sur un VPS dépend d’une activation par le fournisseur et non d’un réglage effectué depuis l’invité.

D’abord, un utilisateur non-root

Créez un utilisateur dédié avec son propre répertoire personnel, puis installez-y votre clé publique :

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

Testez la connexion depuis un second terminal avant d’aller plus loin, tant que l’authentification par mot de passe reste disponible comme solution de secours. Si le message Permission refusée (publickey) s’affiche, le problème vient généralement du propriétaire ou du mode du répertoire .ssh, et non de la clé elle-même.

Par conception, agent ne fait pas partie du groupe sudo. Si un paquet système est nécessaire, installez-le. Cette seule décision élimine la plupart des situations dans lesquelles une commande shell exécutée par erreur peut endommager l’hôte.

Hygiène SSH pour un serveur qui reste allumé

L’authentification par mot de passe sur une machine connectée en permanence à Internet, qui contient un agent et votre code source, représente un risque inutile. Désactivez-la. Sur Ubuntu 24.04 et Debian 13, /etc/ssh/sshd_config inclut /etc/ssh/sshd_config.d/*.conf. Ajoutez donc un fichier au lieu de modifier la configuration principale :

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

Validez la configuration, puis rechargez le service. Gardez votre session actuelle ouverte pendant que vous testez une nouvelle session depuis un second terminal :

sudo sshd -t && sudo systemctl restart ssh

Point particulier sur Ubuntu 24.04 : sshd est activé par socket. Les paramètres d’authentification s’appliquent avec systemctl restart ssh, mais une modification de Port, qui écoute les connexions, nécessite également systemctl daemon-reload et un redémarrage de ssh.socket.

Passez ensuite au pare-feu. Autorisez SSH avant de l’activer, sinon vous vous verrouillez l’accès :

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

Installez fail2ban en sachant précisément ce qu’il apporte : une fois l’authentification par mot de passe désactivée, une attaque par force brute ne peut de toute façon pas aboutir. Cet outil évite que les tentatives échouées remplissent vos journaux.

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

Enfin, appliquez automatiquement les correctifs avec sudo apt install unattended-upgrades et sudo dpkg-reconfigure -plow unattended-upgrades. Tenez compte de l’interaction avec tmux : activez Unattended-Upgrade::Automatic-Reboot et une mise à jour du noyau redémarrera le serveur, ce qui interrompra toutes les sessions. Laissez cette option désactivée et redémarrez selon votre propre planning, lorsqu’aucune tâche n’est en cours d’exécution. La même prudence s’applique à une mise à niveau de version : passer le serveur d’Ubuntu 24.04 à 26.04 redémarre sshd et le noyau. Planifiez donc cette opération pendant une fenêtre où aucune session tmux n’exécute une tâche importante.

Installer Node.js et Claude Code sur Ubuntu

Claude Code est une interface de ligne de commande Node.js. Vous avez donc besoin d’une version récente de Node.js. Le paquet de la distribution est souvent en retard ; NodeSource est la méthode habituelle sur Ubuntu et Debian, et fournit un dépôt signé (sans apt-key, cet outil n’existe plus) :

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

Voici l’erreur la plus fréquente : installez l’interface de ligne de commande avec votre utilisateur agent, jamais avec sudo npm -g. Un préfixe global appartenant à root provoque ensuite des erreurs de permissions et laisse des fichiers appartenant à root dans le cache npm. Configurez d’abord le préfixe npm dans le répertoire personnel de l’utilisateur :

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

L’export doit être placé dans ~/.bashrc, et non dans ~/.profile. Il doit se trouver avant le garde « If not running interactively, don't do anything » situé près du début du fichier : tmux peut démarrer des shells qui ne sont pas des shells de connexion. Ceux-ci lisent ~/.bashrc et ignorent ~/.profile, tandis que ~/.profile n’est exécuté que par les shells de connexion. Un Node.js installé par utilisateur avec un gestionnaire de versions tel que nvm permet d’obtenir le même résultat. Dans les deux cas, l’objectif est que npm install -g n’ait jamais besoin de sudo. npm fonctionne toujours correctement. Vous pouvez aussi utiliser le script d’installation natif d’Anthropic, qui constitue actuellement la méthode documentée par défaut. Consultez la documentation d’installation d’Anthropic avant de le coller, car les méthodes d’installation évoluent.

Exécutez claude dans un dépôt pour le démarrer. La première exécution vous guide dans l’authentification. Une machine sans interface graphique ne dispose pas de navigateur. Le processus vous fournit donc une URL à ouvrir sur votre propre machine, ainsi qu’un code à recopier dans le terminal. (Une clé API définie dans l’environnement constitue l’autre méthode.) Dans les deux cas, ces identifiants résident désormais sur le serveur. C’est le sujet de la prochaine partie.

La question du rayon d’impact

Un agent qui dispose d’un accès shell est un shell. Il peut lire tout ce que l’utilisateur sous lequel il s’exécute peut lire et pousser du contenu partout où cet utilisateur peut le faire. Ce n’est pas un défaut de l’outil : c’est sa définition. C’est pourquoi le compte utilisé pour l’exécuter compte davantage que n’importe quel réglage individuel.

  • Utilisateur dédié et non privilégié. Aucun groupe sudo, aucun répertoire personnel partagé avec votre propre compte.
  • Aucun identifiant de production sur la machine. Aucun ~/.aws/credentials contenant des clés de production, aucun .env copié depuis la production, aucun mot de passe de base de données autorisant l’écriture sur un élément important. Fournissez à l’agent un identifiant de staging ou en lecture seule.
  • Tokens limités. Un token GitHub à portée fine, limité à un seul dépôt ; une deploy key lorsque l’accès en lecture suffit.

Claude Code fournit un flag qui ignore complètement ses demandes d’autorisation. Sur un ordinateur portable ou un projet temporaire, c’est votre choix. Sur un serveur qui contient des tokens, cela supprime le dernier obstacle entre une instruction mal interprétée et un git push --force. Les demandes d’autorisation que vous ignoreriez ne sont pas non plus limitées à deux choix. Avec l’arrivée du mode automatique comme nouveau mode par défaut, il est utile de savoir quel mode d’autorisation doit être imposé sur un serveur que vous ne surveillez pas. Le fonctionnement réel de ce flag et les moyens de contenir un agent qui l’utilise, du sandbox intégré jusqu’à un VPS temporaire, sont présentés dans exécuter Claude Code en toute sécurité sur un serveur.

Deploy key ou transfert de l’agent SSH

Il peut être tentant de ssh -A afin que git puisse utiliser la clé présente sur votre ordinateur portable. Comprenez ce que cela autorise : le transfert de l’agent expose le socket de votre agent SSH local aux processus exécutés sous ce compte sur la machine. Tout processus exécuté sous agent, y compris l’agent, peut demander à votre clé de signer pour n’importe quel hôte qu’il peut atteindre, tant que vous restez connecté. Cela dépasse largement le fait d’« autoriser git à puller ce dépôt ».

Générez plutôt une clé sur le serveur, enregistrez-la comme deploy key pour un dépôt donné (avec un accès en écriture uniquement si l’agent doit pousser), puis définissez une identité git afin que les commits effectués depuis la machine soient identifiables :

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"

Le workflow tmux

Installez-le (sudo apt install tmux), puis créez une configuration minimale avec ~/.tmux.conf :

set -g mouse on
set -g history-limit 50000
set -g default-terminal "tmux-256color"

Quatre commandes suffisent pour l’utilisation quotidienne :

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 est la commande à retenir : elle se rattache à la session si elle existe et la crée sinon. Une seule commande suffit donc pour commencer la journée et reprendre le travail après une déconnexion. Créez un alias pour cette commande. Dans une session, Ctrl-b c ouvre une fenêtre, Ctrl-b n et Ctrl-b p permettent de passer de l’une à l’autre, et Ctrl-b [ active le mode copie pour remonter dans l’historique (q permet d’en sortir).

Retenez un point concernant les sessions que vous ne supprimez jamais : l’agent renvoie l’intégralité de la conversation à chaque tour. Consultez donc ce qu’une session Claude Code exécutée pendant longtemps consomme en tokens avant d’en laisser une active pendant une semaine.

Modes de défaillance

« Ma session a disparu. » tmux ls affiche no server running on /tmp/tmux-1000/default. Cela signifie presque toujours que le processus n’était pas exécuté dans tmux : vous vous êtes connecté en SSH, avez lancé claude directement, puis la déconnexion l’a tué. Il n’y a rien à récupérer. Pour éviter ce problème, exécutez tmux new -A -s <project> comme première commande après chaque connexion.

Le pane se réduit à une toute petite fenêtre. tmux dimensionne une session selon le plus petit client attaché. Un client obsolète resté attaché depuis une autre machine peut donc réduire l’affichage. Forcez la déconnexion des autres clients lors de l’attachement avec tmux attach -d -t claude.

Un build affiche Killed. Un seul message, sans stack trace. Vérifiez avec sudo dmesg -T | grep -i -E 'out of memory|killed process' : l’OOM killer du kernel a sélectionné le processus le plus gourmand. Avec Node, vous pouvez voir à la place FATAL ERROR: Ineffective mark-compacts near heap limit Allocation failed - JavaScript heap out of memory. Procédez dans cet ordre : ajoutez du swap (voir ci-dessus), limitez le parallélisme des tests et du compilateur, augmentez le heap de Node avec NODE_OPTIONS=--max-old-space-size=..., ou augmentez la taille du VPS. L’OOM killer peut aussi sélectionner le serveur tmux au lieu du build et ainsi supprimer votre session. Si systemd-oomd est actif, il peut tuer tout un slice utilisateur avec le même résultat.

npm error code EACCES / permission denied, mkdir '/usr/lib/node_modules/...'. Une installation globale dans un prefix appartenant à root. Utilisez le prefix ~/.npm-global indiqué ci-dessus. Si vous avez déjà exécuté sudo npm, vous pouvez également voir Your cache folder contains root-owned files. Réparez le problème avec sudo chown -R $(id -u):$(id -g) ~/.npm.

claude: command not found, mais seulement parfois. Votre export PATH se trouve dans ~/.bashrc, sous la protection « If not running interactively, don't do anything ». Les shells non interactifs l’ignorent donc. Déplacez l’export au-dessus de cette protection et laissez-le dans ~/.bashrc, pas dans ~/.profile : tmux peut lancer des shells qui ne sont pas des shells de login. Ceux-ci lisent ~/.bashrc et ne consultent jamais ~/.profile.

Couleurs illisibles après l’attachement. Il s’agit d’une incompatibilité de TERM. La ligne default-terminal ci-dessus corrige le problème.

Les sessions disparaissent après un redémarrage. Ce n’est pas un bug : le serveur tmux est un processus, et un redémarrage le termine. Vérifiez uptime.

Ce qui se dégrade avec la croissance

Plus de projets. Utilisez une session tmux par dépôt et nommez-la d’après le dépôt ; tmux ls devient alors votre tableau de bord. Si vous ne suivez pas cette règle de nommage, vous obtenez les sessions 0, 1 et 2. Lorsqu’elles sont plusieurs à fonctionner en parallèle, elles n’ont pas besoin de rester isolées, car une session peut envoyer un message à une autre sur la même machine. C’est utile lorsqu’un agent qui exécute un refactoring long demande à un autre de lancer les tests. Les ports se multiplient de la même manière. Lorsque six dépôts veulent tous utiliser :3000, il est temps d’arrêter de les attribuer manuellement et de laisser un reverse proxy Traefik router plusieurs applications sous Docker Compose en fonction du nom d’hôte.

Plusieurs utilisateurs. Les sockets tmux sont propres à chaque utilisateur. Deux développeurs connectés à la même machine disposent donc chacun de leur propre serveur tmux et ne peuvent pas voir les sessions de l’autre. Partager une session via une socket commune signifie que tout le monde saisit des commandes dans le même shell, avec le même utilisateur Unix, et avec les conséquences que cela implique pour l’audit et les permissions. Des utilisateurs distincts constituent la réponse simple et correcte.

Travail sans supervision. tmux sert aux sessions interactives auxquelles vous vous attachez. Les tâches planifiées qui s’exécutent sans supervision doivent utiliser une unité et un timer systemd. Elles bénéficient ainsi de la journalisation, d’une stratégie de redémarrage et d’une reprise automatique au boot. Utiliser tmux pour exécuter une tâche de type cron indique que cette tâche devrait être un service.

Dernier point : liez les serveurs de développement lancés par l’agent à 127.0.0.1, et non à 0.0.0.0, puis accédez-y via un tunnel SSH (ssh -L 3000:127.0.0.1:3000 agent@your-server) au lieu d’ouvrir des ports dans ufw. Si vous redirigez déjà une demi-douzaine de ports, ou si un téléphone et un ordinateur portable doivent accéder simultanément au même aperçu, placez plutôt un VPN WireGuard auto-hébergé sur le VPS devant ces serveurs : les serveurs de développement se lient à une interface privée et ufw continue de refuser toutes les connexions provenant de l’interface publique. Le pare-feu n’est utile que si vous cessez d’y créer des ouvertures.

Claude Code n’est pas la seule option : exécuter un agent d’IA de programmation sur un VPS permet également d’évaluer Aider et Goose.

FAQ

Claude Code continue-t-il à fonctionner après la coupure de ma connexion SSH ?

Seulement si vous l’avez lancé dans tmux. Un processus lancé directement depuis le shell SSH est un enfant de ce shell et s’arrête avec le pty lorsque la connexion est coupée. Dans tmux, le shell appartient au serveur tmux détaché. L’agent continue donc son travail et tmux attach vous ramène à la même sortie historique. Faites de tmux new -A -s <project> la première commande après chaque connexion. Le problème disparaîtra.

Dois-je installer la CLI avec sudo npm install -g ?

Non. Un prefix global détenu par root provoque des erreurs EACCES lors des installations suivantes et crée des fichiers détenus par root dans le cache npm. Définissez le prefix de npm sur ~/.npm-global (ou utilisez un gestionnaire de versions comme nvm), installez les paquets avec l’utilisateur non privilégié agent, puis ajoutez ~/.npm-global/bin à PATH depuis ~/.bashrc, avant le garde-fou interactif. Si vous avez déjà exécuté sudo npm, corrigez le cache avec sudo chown -R $(id -u):$(id -g) ~/.npm.

Le transfert d’agent ssh -A est-il sûr sur une machine qui exécute un agent ?

Il accorde bien plus de droits que nécessaire. Le transfert expose le socket de votre agent SSH local à chaque processus exécuté avec cet utilisateur. Tout processus présent sur la machine peut donc demander à votre clé de signer des connexions vers n’importe quel hôte accessible, tant que votre session reste attachée. Générez une clé ed25519 sur le serveur et enregistrez-la comme deploy key pour le dépôt concerné. N’accordez l’accès en écriture que si l’agent doit réellement effectuer des push.

Pourquoi mon build affiche-t-il simplement Killed ?

Un seul mot sans stack trace indique généralement l’action de l’OOM killer du noyau. Confirmez-le avec sudo dmesg -T | grep -i -E 'out of memory|killed process' ; depuis Node, vous pouvez voir JavaScript heap out of memory à la place. Appliquez les correctifs dans l’ordre : ajoutez un swapfile, limitez le parallélisme des tests et du compilateur, augmentez NODE_OPTIONS=--max-old-space-size=..., puis redimensionnez le VPS. Attention : l’OOM killer peut sélectionner le serveur tmux au lieu du build et fermer toute votre session.

tmux ou un service systemd ?

tmux convient aux sessions interactives auxquelles vous vous attachez, que vous surveillez et dans lesquelles vous saisissez des commandes. C’est exactement le cas d’une session d’agent. Une tâche exécutée selon un calendrier, sans personne pour la surveiller, doit être gérée par une unité et un timer systemd. Vous bénéficiez ainsi directement de la journalisation, d’une politique de redémarrage et de la reprise après le boot. Si vous utilisez tmux pour exécuter une tâche de type cron, cette tâche devrait être un service.