SSD Nodes Learn Hosting plans →
Руководства Matt ConnorАвтор: Matt Connor

Как запустить Claude Code на удаленном VPS через tmux

Запуск Claude Code на Linux VPS внутри tmux предотвращает завершение сессии при разрыве SSH. Узнайте, как настроить среду, избежать ошибки SIGHUP и обеспечить работу агента.

Проблема в крышке ноутбука, а не в CLI

Claude Code работает на вашем ноутбуке нормально, пока вы его не закроете: SSH-сессия прерывается, оболочка получает сигнал SIGHUP, и агент, выполняющий тест уже три минуты, завершается вместе с ней. Запускайте CLI на машине, которая не уходит в спящий режим, внутри терминального мультиплексора, процессы которого не являются дочерними по отношению к вашей SSH-сессии. В этом весь секрет, и именно tmux, а не установка, является ключевым компонентом системы.

Эта страница посвящена эксплуатации сервера, на котором вы оставляете работать агентов. Если у вас нет Linux-сервера, который можно оставить включенным, эти рекомендации к вам не относятся. Это единственное обязательное условие.

Принцип работы tmux

При подключении по SSH sshd создает процесс оболочки и выделяет ей псевдотерминал; все запущенные из этой оболочки процессы являются её дочерними. При разрыве соединения ядро закрывает pty, оболочка получает сигнал SIGHUP и завершает свои дочерние процессы. Длительные процессы, запущенные в интерактивном режиме, прекращают работу.

tmux меняет эту модель владения. Команда tmux, которую вы вводите, — это легковесный клиент, взаимодействующий через unix-сокет с сервером tmux, который работает независимо от вашего терминала. Оболочки внутри сессии являются дочерними процессами этого сервера, а не sshd. При завершении SSH-сессии клиент закрывается, но сервер, сессия и выполняемая задача продолжают работу. При повторном подключении команда tmux attach возвращает вас в ту же оболочку с сохраненной историей прокрутки. Процесс nohup также переживает разрыв соединения, но не предоставляет способа вернуться в него; вы не можете повторно подключиться к работающему в фоне TUI. Claude Code является интерактивным инструментом; tmux (или screen) — подходящее решение для таких задач.

Выбор размера сервера

CLI представляет собой единый нативный бинарный файл; он не является тем, что перегружает машину. Машину перегружает то, что агент запускает от вашего имени: сборка, полный набор тестов, tsc, языковой сервер или база данных в Docker. Выбирайте ресурсы под инструментарий, а не под CLI. Добавьте swap, даже если планируете никогда его не использовать: это превратит жесткое завершение процесса по OOM в медленную сборку:

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

Следите за диском: репозитории, node_modules и образы Docker быстро накапливаются. Если инструментарий выходит за пределы контейнеров и использует полноценные виртуальные машины, KVM-гости или локальный узел Kubernetes, убедитесь, что тарифный план предоставляет доступ к расширениям виртуализации CPU до того, как вы оформите подписку. Запуск вложенной виртуализации на VPS — это функция, которую провайдер должен включить для вас, а не то, что можно активировать изнутри гостевой системы.

Первичная настройка пользователя без прав root

Создайте выделенного пользователя с собственным домашним каталогом и разместите в нём свой публичный ключ:

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

Проверьте возможность входа со второго терминала, прежде чем продолжать работу, пока аутентификация по паролю доступна в качестве резервного варианта. Если система возвращает ошибку Permission denied (publickey), проблема обычно заключается в правах доступа или владельце каталога .ssh, а не в самом ключе.

Умышленно agent не включен в группу sudo. Если требуется системный пакет, вы устанавливаете его самостоятельно. Это решение устраняет большинство способов, которыми случайная команда оболочки может повредить хост.

Гигиена SSH для сервера, работающего в постоянном режиме

Использование парольной аутентификации на машине, которая постоянно находится в публичном доступе, содержит SSH-агент и ваш исходный код — неоправданный риск. Отключите её. В Ubuntu 24.04 и Debian 13 конфигурация /etc/ssh/sshd_config включает /etc/ssh/sshd_config.d/*.conf, поэтому лучше создать отдельный файл, чем редактировать основной конфигурационный файл:

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

Не добавляйте обмен ключами в этот файл без конкретной причины: строка KexAlgorithms, скопированная из старого руководства по усилению защиты, часто фиксирует только классические алгоритмы, из-за чего современный SSH-клиент выводит предупреждение о том, что соединение не использует постквантовый обмен ключами.

Проверьте конфигурацию и перезагрузите службу, не закрывая текущую сессию, чтобы протестировать новую сессию из другого терминала:

sudo sshd -t && sudo systemctl restart ssh

Нюанс для Ubuntu 24.04: sshd активируется через сокет. Настройки аутентификации применяются в systemctl restart ssh, но для изменения порта прослушивания Port также требуется systemctl daemon-reload и перезапуск ssh.socket.

Затем настройте межсетевой экран. Разрешите SSH до того, как включите его, иначе вы потеряете доступ к серверу:

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

Устанавливайте fail2ban, четко понимая, зачем это нужно: когда парольная аутентификация отключена, брутфорс в любом случае не увенчается успехом, а этот инструмент просто избавит ваш журнал от записей о неудачных попытках входа.

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

Наконец, настройте автоматическое обновление с помощью sudo apt install unattended-upgrades и sudo dpkg-reconfigure -plow unattended-upgrades. Учтите взаимодействие с tmux: если включить Unattended-Upgrade::Automatic-Reboot, обновление ядра перезагрузит сервер и завершит все сессии. Оставьте эту опцию выключенной и выполняйте перезагрузку по собственному графику, когда никакие процессы не запущены. Та же осторожность требуется при обновлении релиза: переход сервера с Ubuntu 24.04 на 26.04 приводит к перезапуску sshd и ядра, поэтому такие операции следует выполнять в окне обслуживания, когда в tmux нет важных незавершенных задач.

Установка Node.js и Claude Code в Ubuntu

Для использования npm требуется Node.js версии 22 или выше. (Пакет теперь загружает нативный бинарный файл, который не выполняется средствами Node, но npm всё равно проверяет версию движка, а инструментарий агента обычно требует актуальную версию Node). Пакеты из дистрибутива часто устаревают; NodeSource — стандартный способ установки в Ubuntu и Debian. Он предоставляет подписанный репозиторий (без apt-key, который признан устаревшим в Ubuntu 24.04 и уже удален из Debian 13):

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

Теперь этап, на котором часто совершают ошибки: устанавливайте CLI от имени вашего agent пользователя, никогда не используйте sudo npm -g. Глобальный префикс, принадлежащий root, приводит к ошибкам прав доступа в будущем и оставляет файлы, принадлежащие root, в кэше npm. Сначала укажите npm префикс в домашней директории пользователя:

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

Команда export добавляется в ~/.bashrc, и её следует разместить перед блоком «Если оболочка не интерактивная, ничего не делать» в начале файла. Обычная панель tmux запускает login shell: tmux оставляет default-command пустым, что заставляет его запускать default-shell как login shell, а login bash считывает ~/.profile, который в Ubuntu подключает ~/.bashrc. Non-login оболочки, например, если вы сами настроили default-command, считывают ~/.bashrc напрямую. Таким образом, ~/.bashrc охватывает оба случая, в то время как одного ~/.profile было бы недостаточно. Важный нюанс: bash пропускает ~/.profile, если существуют ~/.bash_profile или ~/.bash_login, поэтому, если вы создали один из них, добавьте в него вызов ~/.bashrc. Использование Node для конкретного пользователя через менеджер версий, такой как nvm, дает тот же результат; цель в любом случае состоит в том, чтобы npm install -g никогда не требовал sudo. npm по-прежнему работает корректно, но в документации Anthropic нативный скрипт установки указан как рекомендуемый путь, а подписанные репозитории apt, dnf и apk являются альтернативой. Перед выполнением команд сверьтесь с документацией по установке Anthropic, так как методы могут меняться.

Запустите claude внутри репозитория, чтобы начать работу. Первый запуск проведет вас через процесс аутентификации; на headless-сервере нет браузера, поэтому система предоставит URL, который нужно открыть на вашем локальном компьютере, и код, который нужно ввести в терминале. (Другой вариант — использование API-ключа в переменных окружения). В любом случае, эти учетные данные теперь будут храниться на сервере, что подводит нас к части, которую часто пропускают.

Обсуждение радиуса поражения

Агент с доступом к shell — это и есть shell. Он может читать всё, что доступно пользователю, от имени которого он запущен, и записывать данные везде, где у этого пользователя есть права на запись. Это не критика инструмента, а его определение. Именно поэтому учетная запись, под которой работает агент, важнее любых отдельных настроек.

  • Выделенный непривилегированный пользователь. Никаких групп sudo, никакой общей домашней директории с вашей личной учетной записью.
  • Отсутствие производственных учетных данных на сервере. Никаких ~/.aws/credentials с ключами от продакшена, никаких .env, скопированных из рабочей среды, никаких паролей от баз данных с правами записи к критически важным ресурсам. Предоставьте агенту учетные данные для staging-среды или права только на чтение.
  • Ограниченные токены. Используйте токен GitHub с узкими правами, ограниченный одним репозиторием; используйте deploy key, если достаточно доступа только на чтение.

Claude Code поддерживает флаг --dangerously-skip-permissions, который пропускает почти все запросы на подтверждение действий. На личном ноутбуке или в одноразовом проекте — это ваш выбор. На сервере, где хранятся токены, этот флаг убирает последний барьер между неверно истолкованной инструкцией и git push --force. Запросы, которые вы пропускаете, не являются принципом «всё или ничего», а учитывая, что автоматический режим теперь является режимом запуска по умолчанию (начиная с Claude Code v2.1.283), важно понимать, какой режим разрешений должен быть зафиксирован на сервере, за которым вы не наблюдаете постоянно. О том, что именно меняет этот флаг и как ограничить агента, работающего с ним — от встроенной песочницы до одноразового VPS, — рассказано в разделе безопасный запуск Claude Code на сервере.

Deploy key против SSH agent forwarding

Возникает соблазн использовать ssh -A, чтобы git мог использовать ключ с вашего ноутбука. Поймите, что это дает: пересылка агента (agent forwarding) открывает доступ к сокету вашего локального SSH-агента процессам, запущенным от имени пользователя на удаленной машине. Любой процесс, работающий как agent, включая агента, может потребовать от вашего ключа подписи для любого хоста, к которому он может подключиться, пока вы остаетесь в сети. Это гораздо больше, чем просто «разрешить git стянуть один репозиторий».

Вместо этого сгенерируйте ключ на сервере, зарегистрируйте его как deploy key для конкретного репозитория (права на запись — только если агенту нужно выполнять push) и настройте git identity, чтобы коммиты с этого сервера можно было идентифицировать:

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"

Рабочий процесс в tmux

Установите его (sudo apt install tmux), затем создайте минимальный ~/.tmux.conf:

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

Четыре команды покрывают ежедневные задачи:

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 — это команда, которую нужно запомнить: она подключается к существующей сессии или создает новую, если та отсутствует. Таким образом, одна команда подходит и для начала работы, и для восстановления после разрыва соединения. Создайте для неё алиас. Внутри сессии Ctrl-b c открывает окно, Ctrl-b n и Ctrl-b p переключаются между ними, а Ctrl-b [ активирует режим копирования для прокрутки истории (q выходит из него).

Важный момент относительно сессий, которые вы никогда не завершаете: агент повторно отправляет всю историю переписки при каждом запросе. Поэтому ознакомьтесь с тем, на что расходуются токены в длительной сессии Claude Code, прежде чем оставлять её запущенной на неделю.

Типичные сбои

«Моя сессия пропала». tmux ls выводит no server running on /tmp/tmux-1000/default. Это почти всегда означает, что процесс не был запущен внутри tmux: вы подключились по SSH, запустили claude напрямую, и разрыв соединения завершил его работу. Восстановить такой процесс невозможно. Привычка, которая предотвращает это: tmux new -A -s <project> — это первая команда после каждого входа в систему.

Панель сжимается до крошечного размера. Опция window-size определяет, какой подключенный клиент задает размер. Начиная с tmux 3.1 (в Ubuntu 24.04 поставляется 3.4), значением по умолчанию является latest — клиент с последней активностью. Однако если в конфигурации указано window-size smallest, размер подстраивается под самого «маленького» подключенного клиента, поэтому неактивный клиент, оставшийся подключенным с другого устройства, сжимает отображение. Принудительно отключайте остальных при подключении: tmux attach -d -t claude.

Сборка выводит Killed. Одно слово, без трассировки стека. Проверьте с помощью sudo dmesg -T | grep -i -E 'out of memory|killed process': это означает, что OOM killer ядра выбрал самый ресурсоемкий процесс. В Node вы можете увидеть FATAL ERROR: Ineffective mark-compacts near heap limit Allocation failed - JavaScript heap out of memory. Способы исправления в порядке приоритета: добавьте swap (см. выше), ограничьте параллелизм тестов и компилятора, увеличьте кучу Node с помощью NODE_OPTIONS=--max-old-space-size=... или увеличьте ресурсы VPS. OOM killer может также выбрать сервер tmux вместо процесса сборки, что приведет к завершению сессии. Если запущен systemd-oomd, он может завершить весь пользовательский слайс с тем же результатом.

npm error code EACCES / permission denied, mkdir '/usr/lib/node_modules/...'. Глобальная установка в директорию, принадлежащую root. Используйте префикс ~/.npm-global, указанный выше. Если вы уже запускали sudo npm, вы также можете увидеть Your cache folder contains root-owned files; исправьте это с помощью sudo chown -R $(id -u):$(id -g) ~/.npm.

claude: command not found, но только иногда. Ваш экспорт PATH находится в ~/.bashrc после блока «Если оболочка не интерактивна, ничего не делать», поэтому неинтерактивные оболочки пропускают его. Переместите экспорт выше этого блока и оставьте его в ~/.bashrc: стандартная панель tmux — это login shell, который обращается к ~/.bashrc через ~/.profile в Ubuntu, а любая не-login оболочка считывает ~/.bashrc напрямую, поэтому один этот файл покрывает оба случая.

Искаженные цвета после подключения. Несоответствие TERM, строка default-terminal выше решает эту проблему.

Сессии исчезают после перезагрузки. Это не ошибка: сервер tmux — это процесс, и перезагрузка завершает его работу. Проверьте uptime.

Что ломается при масштабировании

Больше проектов. Одна сессия tmux на репозиторий с соответствующим именем; tmux ls в таком случае служит вашей панелью управления. Если пренебречь дисциплиной именования, вы получите сессии 0, 1, 2. Когда запущено сразу несколько сессий, они не обязаны работать изолированно, так как одна сессия может передать сообщение другой на том же сервере. Это полезно, когда агент, выполняющий длительный рефакторинг, хочет запустить тесты в другом процессе. Порты разрастаются аналогичным образом: когда шесть репозиториев требуют :3000, пора прекратить назначать их вручную и позволить reverse proxy Traefik для маршрутизации нескольких приложений в Docker Compose выполнять распределение по именам хостов.

Больше людей. Сокеты tmux привязаны к пользователю, поэтому два разработчика на одном сервере получают собственные серверы tmux и не видят сессии друг друга. Совместное использование одной сессии через общий сокет означает, что все вводят команды в одну оболочку от имени одного пользователя Unix, что влечет за собой последствия для аудита и прав доступа. Раздельные учетные записи пользователей — это скучное, но верное решение.

Фоновые задачи. tmux предназначен для интерактивных сессий, к которым вы подключаетесь. Задачи, которые должны выполняться по расписанию без присмотра, следует оформлять в виде юнита и таймера systemd: так вы бесплатно получаете логирование, политику перезапуска и автоматический запуск при загрузке. Использование tmux для запуска задач по расписанию — признак того, что задача должна быть оформлена как сервис.

Последнее замечание: привязывайте серверы разработки, которые запускает агент, к 127.0.0.1, а не к 0.0.0.0, и обращайтесь к ним через SSH-туннель (ssh -L 3000:127.0.0.1:3000 agent@your-server), вместо того чтобы открывать порты в ufw. Когда вы пробрасываете полдюжины портов или хотите получить доступ к одному и тому же превью с телефона и ноутбука, лучше настроить самостоятельно размещенный VPN WireGuard на VPS: серверы разработки будут привязаны к приватному интерфейсу, а ufw продолжит блокировать все входящие соединения с публичного интерфейса. Межсетевой экран помогает только в том случае, если вы перестанете делать в нем дыры.

Claude Code — не единственный выбор: запуск AI-агента для программирования на VPS также позволяет сравнить Aider и Goose.

FAQ

Продолжает ли Claude Code работать после разрыва SSH-соединения?

Только если вы запустили его внутри tmux. Процесс, запущенный напрямую из SSH-оболочки, является дочерним по отношению к этой оболочке и завершается вместе с pty при разрыве соединения. Внутри tmux оболочка принадлежит отсоединенному серверу tmux, поэтому агент продолжает работу над задачей, а tmux attach возвращает вас к тому же состоянию прокрутки. Сделайте tmux new -A -s <project> первой командой после каждого входа в систему, и проблема исчезнет.

Стоит ли устанавливать CLI с помощью sudo npm install -g?

Нет. Глобальный префикс, принадлежащий root, приводит к ошибкам EACCES при последующих установках и появлению файлов, принадлежащих root, в кэше npm. Установите префикс npm в ~/.npm-global (или используйте менеджер версий, например nvm), выполняйте установку от имени непривилегированного пользователя agent и экспортируйте ~/.npm-global/bin в PATH из файла ~/.bashrc, разместив это до проверки интерактивности. Если вы уже запускали sudo npm, исправьте кэш с помощью sudo chown -R $(id -u):$(id -g) ~/.npm.

Безопасно ли использование перенаправления агента ssh -A на сервере, где запущен агент?

Это предоставляет гораздо больше прав, чем требуется для работы. Перенаправление открывает сокет вашего локального SSH-агента для любого процесса, запущенного от имени этого пользователя. В результате любой процесс на сервере может использовать ваш ключ для подписи запросов к любому доступному хосту, пока вы остаетесь подключенным. Сгенерируйте ключ ed25519 на сервере и зарегистрируйте его как deploy key для конкретного репозитория, предоставив права на запись только в том случае, если агенту действительно нужно выполнять push.

Почему моя сборка просто выводит Killed?

Одно слово без стека вызовов означает, что сработал OOM killer ядра. Подтвердите это с помощью sudo dmesg -T | grep -i -E 'out of memory|killed process'; в Node можно увидеть JavaScript heap out of memory. Выполняйте исправления по порядку: добавьте swapfile, ограничьте параллелизм тестов и компилятора, увеличьте NODE_OPTIONS=--max-old-space-size=..., а затем увеличьте ресурсы VPS. Учтите, что OOM killer может выбрать сервер tmux вместо процесса сборки, что приведет к завершению всей вашей сессии.

tmux или systemd service?

tmux подходит для интерактивных сессий, к которым вы подключаетесь, за которыми наблюдаете и в которых вводите команды — именно так работает сессия агента. Работа, которая выполняется по расписанию без участия пользователя, должна быть оформлена в виде юнита и таймера systemd, где логирование, политика перезапуска и автозапуск при загрузке предоставляются по умолчанию. Если вы используете tmux для запуска задачи по расписанию, эту задачу следует перевести в статус сервиса.