AI-агенты для кода хорошо справляются с инфраструктурной работой, у которой давно известная форма: Dockerfile для приложения на Node или Python, unit systemd, серверный блок Nginx перед приложением, CI-пайплайн, который ставит зависимости, тестирует и собирает, миграция базы данных и тесты вокруг всего этого. Они выдают такое за секунды, и обычно результат близок к верному. Они плохи в деталях, меняющихся между версиями - флагах, ключах конфигурации, значениях по умолчанию - и неверное говорят с той же уверенностью, что и верное. А получив shell, они выполнят разрушительную команду так же спокойно, как ls. Правило, по которому мы работаем, короткое: агент пишет, инструмент проверяет, человек читает каждую команду, а агент никогда не держит продакшен-доступы. Всё ниже - о том, как это правило работает на практике.
AI-агенты для кода стали обычной частью разработки, а работа с серверами на хостинг-платформе вроде RE:NODE даёт нам мнение о том, что с сервером можно делать, а что нельзя. Этот текст - эти мнения. Историй из жизни в нём нет, только принципы, к которым мы пришли.
Где агенты быстры и правы#
Агенты хорошо делают то, что публично писалось десять тысяч раз с небольшими вариациями. Это удивительно большая часть DevOps.
- Dockerfile для типовых стеков. Многоэтапная сборка приложения на Node, Go, Python или .NET с лёгким runtime-образом, не-root пользователем и установкой зависимостей, кэшируемой до копирования исходников. Агенты хорошо знают этот паттерн и расставляют слои правильно чаще, чем уставший человек.
- Unit systemd.
ExecStart,WorkingDirectory,User,Restart=on-failure,EnvironmentFile, секция[Install]. Если попросить усиление защиты, получится разумный набор изProtectSystem=,NoNewPrivileges=и родственных параметров. - Блоки Nginx reverse proxy.
proxy_pass, forwarded-заголовки, заголовки апгрейда WebSocket, редирект с HTTP на HTTPS. Типовой случай решается надёжно. - CI-пайплайны. Workflow GitHub Actions, который забирает код, настраивает runtime, кэширует зависимости, запускает lint и тесты и собирает артефакт. Агенты пишут его быстрее, чем получается найти последний репозиторий, где это уже делалось.
- Миграции и тесты. По схеме и изменению они пишут миграцию и, что полезнее, тесты, фиксирующие поведение. От тестов больше всего пользы на минуту, потому что падающий сгенерированный тест - информация в любом случае.
- Склеивающие скрипты. Помощники ротации логов, обёртки бэкапов, скрипт проверки срока сертификата. Короткий Bash и Python с понятными входами и выходами.
Общее у них: форма стабильна годами, ошибки видны сразу при запуске или проверке, а цена неверного первого черновика - минута просмотра. Это зона, где агент - однозначный выигрыш, и возвращаться к ручному написанию такого не хочется.
Есть и второй, менее заметный выигрыш: чтение. Агент, которому разрешено читать репозиторий, каталог конфигурации и несколько логов, а затем объяснять происходящее, часто быстрее, чем самостоятельный поиск по системе, которой касались месяцы назад. Чтение ничего не может сломать. Подробнее об этом ниже.
Где агенты уверенно ошибаются#
Сбой не в том, что агенты выдают бессмыслицу. Они выдают правдоподобную конфигурацию, которая была верной когда-то, для какой-то версии, на каком-то дистрибутиве, и ничто в выводе не говорит, какая часть устарела.
За какими шаблонами мы следим:
- Выдуманные флаги. Флаг CLI, который звучит так, будто должен существовать, в стиле именования инструмента, но которого нет. Он выглядит точно так же, как настоящие флаги вокруг.
- Устаревшие ключи. Конфигурация, которая была верной несколько мажорных версий назад. Модель видела гораздо больше старой документации, чем новой, поэтому старые формы представлены чаще.
- Смешанные версии. Файл, где половина директив из одной мажорной версии, а половина из другой. Каждая строка выглядит нормально; такого сочетания никогда не существовало.
- Размытие дистрибутивов. Пути и имена пакетов из Debian в файле для машины семейства RHEL или наоборот.
- Молчаливые значения по умолчанию. Опция, явно выставленная в то, что агент считает значением по умолчанию, хотя оно изменилось, или пропущенная важная опция, потому что агент решил, что значение по умолчанию есть, а его нет.
Несколько настоящих примеров дрейфа, которые агенты воспроизводят и которые можно сверить с upstream-документацией:
| Что пишет агент | Актуальная форма | Почему это важно |
|---|---|---|
listen 443 ssl http2; | listen 443 ssl; и http2 on; | Nginx 1.25.1 объявил параметр устаревшим |
ssl on; | ssl в строке listen | Устарело с Nginx 1.15.0, позже удалено |
version: "3.8" в Compose | не указывать | Compose игнорирует и предупреждает |
MemoryLimit= в unit | MemoryMax= | Старое имя из эпохи cgroup v1 |
apt-key add | keyring в signed-by= | apt-key объявлен устаревшим |
echo "::set-output ..." | запись в $GITHUB_OUTPUT | GitHub объявил команду устаревшей |
Ничто из этого не экзотика. В этом и суть. Неверная форма - та, которой годами пользовалась большая часть интернета, поэтому модель тянется к ней первой. Часть таких вариантов всё ещё работает с предупреждением, и это хуже отказа: файл попадает в репозиторий, предупреждение уходит в лог, который никто не читает, а следующее обновление удаляет старую форму.
Уверенность агента ничего не говорит о его правоте. Неверный ключ конфигурации и верный приходят с одинаково спокойной интонацией.
Неприятно и то, что агент будет защищать неверный ответ, если спросить "ты уверен?". Иногда он исправляется, иногда упорствует, а иногда извиняется и превращает верную строку в неверную. Вопрос к модели - не проверка. Проверка - запуск собственного валидатора инструмента.
Разрушительные команды и почему опаснее всего shell#
Запись файла легко отменить. Выполнение команды часто нет. Переход от "агент правит файлы в репозитории" к "агент выполняет команды на машине" - место настоящего риска, и люди делают этот шаг особенно небрежно.
Команды, которые мы считаем "никогда без человека", кто бы их ни предлагал:
rm -rfс переменной в пути. Если переменная пуста,rm -rf "$DIR/"превращается вrm -rf /. Агенты пишут такой шаблон, потому что его пишут люди.docker system prune -a --volumesилиdocker volume rm. В томах живут базы данных. "Освободи место на диске" - вполне разумная просьба, которая заканчивается пустой базой.docker compose down -v. Ключ-vудаляет именованные тома. Выглядит как безобидный флаг порядка.git reset --hard,git clean -fdx,git push --force. Нормально на черновой ветке, губительно на общей или на сервере с незакоммиченной локальной конфигурацией.DROP,TRUNCATE,DELETEбезWHEREили миграция, которая удаляет столбец в том же релизе, где перестаёт в него писать.chown -Rиchmod -Rне из того каталога.- Изменения firewall по SSH.
ufw enableдоufw allow OpenSSHилиiptables -Fдля цепочки с политикой по умолчаниюDROP- и сессия, в которой вы печатаете, пропала. terraform applyилиansible-playbookбез предварительного просмотра плана или проверочного прогона.
Допустим, агента просят исправить "диск заполнен" на машине в три часа ночи. Быстрее всего освободить место, удалив самое большое, а самое большое на сервере очень часто - те данные, которые вы обязаны хранить. Человек бы тут засомневался. Агент, оптимизирующий "задача выполнена", не сомневается, если не устроить всё так, чтобы он не мог.
Случай с firewall заслуживает отдельной заметки, потому что он разрушает не данные, а доступ. Если агента вообще подпускают к firewall, безопасный порядок такой: сначала разрешить SSH и держать вторую сессию открытой на время включения - эта последовательность описана в руководстве по UFW.
Ограничения: сначала только чтение#
Самое полезное ограничение ничего не стоит: начинайте каждую инфраструктурную сессию с того, что агент читает, а не делает.
Чтение - это просмотр файлов конфигурации, список сервисов, чтение логов, запуск команд статуса. На сервере это такие вещи, как systemctl status, journalctl -u app --since "1 hour ago", ss -tlnp, df -h, docker ps, nginx -T. Ни одна из них не меняет состояние. Дайте агенту составить картину и предложить изменение в виде diff или списка команд, а решайте сами.
В Claude Code есть режим планирования, в котором он читает и предлагает без правок, а и Claude Code, и Cursor можно настроить так, чтобы они спрашивали перед запуском команд в терминале. Для всего, что касается инфраструктуры, оставляйте это включённым. В Claude Code можно пойти дальше с правилами разрешений в settings.json: разрешить команды только для чтения, чтобы они не спрашивали, и запретить те, что не должны выполняться без присмотра:
{ "permissions": { "allow": [ "Bash(systemctl status:*)", "Bash(journalctl:*)", "Bash(docker ps:*)", "Bash(nginx -t)", "Bash(git diff:*)" ], "deny": [ "Bash(rm -rf:*)", "Bash(docker system prune:*)", "Bash(git push --force:*)", "Read(./.env)", "Read(./secrets/**)" ] }}Список запретов - ремень безопасности, а не стена. Команду можно сформулировать по-разному, а настойчивая цепочка shell обойдёт совпадение по префиксу. Относитесь к этим правилам как к способу остановить типичную случайность, а настоящую защиту держите там, где ей место: в том, до чего среда агента вообще может дотянуться.
Ограничения: пробные прогоны, просмотр и копии для staging#
После режима чтения следующий слой - чтобы любое изменение сначала показывало, что оно сделает. У большинства серьёзных инфраструктурных инструментов для этого есть режим, и агенту стоит каждый раз велеть им пользоваться.
$ terraform plan -out=change.plan$ ansible-playbook site.yml --check --diff$ rsync -av --delete --dry-run ./site/ deploy@host:/var/www/site/$ kubectl apply -f deploy.yaml --dry-run=server$ kubectl diff -f deploy.yamlДальше читайте каждую команду перед запуском. Каждую. Это звучит медленно, но не медленно, потому что большинство команд в сессии - чтение, а прочитать однострочную команду - секунды. Те, что меняют состояние, стоят этих секунд. Как только заметите, что подтверждаете, не читая, переведите агента обратно в режим, где он просит план.
Просматривайте diff, а не резюме. Агенты хорошо пишут резюме своих изменений, и иногда резюме не совпадает с diff. "Обновлена конфигурация Nginx для добавления кэширования" может идти вместе с удалённым блоком location, о котором не сказано. Источник истины - git diff.
Последний слой - где работает агент. Агенты должны работать с локальной копией репозитория, контейнером или копией staging, но никогда не напрямую с продакшеном. Копия staging - вторая среда той же формы, что и продакшен, со своими доступами и своими данными, где неверная команда стоит пересборки, а не извинений. О практической стороне написано в статье staging и продакшен на одном аккаунте и, для игровых серверов, в тестовый сервер рядом с продакшеном. Там агент может работать так быстро, как хочет. Продвижение в продакшен - отдельный скучный шаг с проверкой человеком: merge и пайплайн деплоя, а не агент с SSH.
Никогда не давайте агенту продакшен-доступы#
Если оставить только одно правило, пусть будет это.
Сессия агента читает много текста, который вы не писали: исходники зависимостей, README, комментарии в issue, веб-страницы, строки логов, сообщения об ошибках сторонних сервисов. Любой из них может содержать инструкции, а современные модели можно направлять инструкциями, спрятанными в читаемом содержимом. Это prompt injection, и полного решения ни у кого нет. Если добавить к нему доступы и shell, вопрос не в том, добронамеренен ли агент, а в том, добронамерен ли каждый кусок текста, который он прочитает.
Поэтому практические правила:
- Никаких URL продакшен-баз, облачных ключей, SSH-ключей к продакшен-хостам и токенов деплоя в окружении агента. Ни в
.env, который он может прочитать, ни в истории shell, ни вставленными в чат "только в этот раз". - Агент работает с доступами staging, ограниченными staging, которые можно слить без большого вреда.
- Деплой в продакшен идёт через CI со своими секретами, запускаемый merge, одобренным человеком. Агент может написать пайплайн. Запускать его он не должен.
- Если агенту нужно посмотреть на продакшен, он читает выгруженные логи или метрики либо человек выполняет команду только для чтения и вставляет вывод.
Если серверы управляются через панель, а не по чистому SSH, минимальные привилегии работают так же: давайте каждому человеку и каждому инструменту только нужный доступ и только на нужное время. Идея изложена в субпользователи и минимальные привилегии, а работа с самими секретами - в переменные окружения и секреты.
Как проверять сгенерированную конфигурацию#
У каждого достойного формата конфигурации есть проверяющий инструмент, и главное - запускать его до того, как что-либо перезагрузится. Просите агента запускать валидатор как часть изменения, а его вывод читайте сами.
| Файл | Чем проверять | Что ловит |
|---|---|---|
| Конфигурация Nginx | nginx -t | Синтаксис, неизвестные директивы |
| Unit systemd | systemd-analyze verify app.service | Неизвестные ключи, неверные пути |
| Dockerfile | hadolint Dockerfile | Плохие практики, фиксация версий |
| Файл Compose | docker compose config | Схема, подстановки |
| GitHub Actions | actionlint | Выражения, устаревший синтаксис |
| Shell-скрипт | shellcheck script.sh | Кавычки, пустые переменные |
| Конфигурация sshd | sshd -t | Синтаксис до того, как вы заперли себя снаружи |
| sudoers | visudo -c | Сломанный файл, убивающий sudo |
Валидация ловит синтаксис и неизвестные ключи. Она не ловит конфигурацию, которая валидна и неверна: proxy_pass не на тот порт, unit, который стартует от root, потому что пропущен User=, кэш, сохраняющий ответы с cookie. Для этого вручную проверяются три вещи:
- Каждый незнакомый флаг и ключ сверяется с upstream-документацией для реально установленной версии. Сначала
nginx -v,docker --version,systemctl --version, затем документация этой версии. Здесь умирают выдуманные флаги. - Что происходит при сбое. Что будет, если приложение упадёт, upstream недоступен, диск заполнится. Unit с
Restart=alwaysи без ограничений бесконечно перезапускает сломанное приложение; это может быть тем, что нужно, а может и нет. - От кого запускается и до чего может дотянуться. Пользователь, права на файлы, открытые порты, смонтированные тома. Агенты по умолчанию стремятся к рабочему, а не к закрытому, так что настройка безопасности остаётся за человеком.
Затем изменение уходит в staging и там проверяется в деле: сервис чисто перезапускается, проверка работоспособности проходит, логи тихие. Только после этого тот же файл идёт в продакшен обычным путём. Для конкретных форматов сверять сгенерированное стоит с руководством по systemd, руководством по Nginx reverse proxy и Docker Compose для небольших стеков.
Миграциям, CI и тестам - больше подозрительности#
Эти три вещи выглядят как обычный код, поэтому проходят обычный review. Они заслуживают большего.
Миграции - это код, который выполняется один раз над единственной копией ваших данных. Агент охотно напишет миграцию, переименовывающую столбец, что в живой системе для работающего приложения равно удалению и добавлению. Он напишет backfill, блокирующий большую таблицу. Он напишет down-миграцию, которая не может восстановить то, что удалила up. Наше правило: каждая сгенерированная миграция выполняется на восстановленной копии данных реального размера, прежде чем приблизиться к продакшену, а разрушающие изменения разносятся по релизам - перестать использовать столбец, выкатить, затем удалить. Миграции без простоя описывают шаблон expand-and-contract, и агента стоит явно заставлять ему следовать, потому что по умолчанию он этого не делает. А восстановленная копия существует только если ваши бэкапы восстанавливаются, и это отдельная дисциплина: бэкапы, которые действительно восстанавливаются.
CI-пайплайны работают с секретами. Сгенерированный workflow, использующий pull_request_target, забирающий код pull request и запускающий его, отдал секреты репозитория всем, кто откроет pull request. Workflow, фиксирующий сторонние actions по подвижному тегу, а не по коммиту, доверяет тому, кто этим тегом управляет. Агенты выдают оба шаблона, потому что оба распространены в публичных репозиториях. Читайте триггеры и блок разрешений у каждого сгенерированного workflow и явно задавайте permissions: по минимуму.
Тесты - самое тонкое. Агенту, которого просят "сделай, чтобы тесты проходили", доступны два пути: исправить код или поменять тест. Иногда он молча выбирает второй - ослабляет проверку, добавляет пропуск, подменяет моком именно то, что тестируется. Зелёный набор тестов от агента не доказательство, пока вы не посмотрели, что изменилось в тестах. При просмотре diff агента изменения в тестах читаются первыми.
Как устроен цикл работы с AI#
Когда агенты - основной цикл сборки, а не помощник, работа человека меняется. Писать приходится меньше, просматривать больше, и просмотр и есть настоящая работа. Принципы, которым подчиняется цикл:
- Небольшие изменения, которые легко проверить. Одна задача на изменение. Агент с удовольствием отрефакторит скрипт деплоя, Dockerfile и CI-пайплайн за один заход; лучше три diff, каждый из которых читается за несколько минут.
- Утверждения агента - не доказательство. "Проверено" ничего не значит, пока не виден запуск команды и её вывод. "Этот флаг существует" ничего не значит, пока не видна документация.
- Валидаторы и тесты внутри цикла, а не после него. Агент сам запускает
nginx -t,shellcheck, набор тестов и читает ошибки. Это ловит большую часть дрейфа ещё до того, как человек увидит diff. - Инфраструктурные изменения идут тем же пайплайном, что и код. Никаких срочных правок на серверах руками агента. Если изменение стоит делать, оно стоит коммита.
- За merge и продакшен отвечают люди. Агент может предложить что угодно. Выпустить он не может ничего.
- Записывайте правила проекта для агента. Короткий файл инструкций в репозитории - какие команды безопасны, какие среды существуют, чего никогда не трогать - работает лучше, чем напоминания в каждой сессии.
Отдача реальна. Типовая инфраструктура, которая раньше съедала полдня, теперь - несколько минут просмотра, и это время уходит на то, что требует суждения: каковы режимы отказа, где живут данные, у кого есть доступ. Агент ускоряет простую часть. Сложная не исчезает, и делать вид, что исчезает, - способ превратить быстрый цикл в быстрый способ всё сломать.
Что не стоит отдавать агенту#
Кое-что остаётся за человеком не потому, что агент не способен выдать результат, а потому что цена правдоподобного неверного ответа слишком высока и его слишком трудно заметить:
- Реагирование на инцидент в живой системе. Агент под давлением "почини немедленно" - ровно та обстановка, где возможен разрушительный сокращённый путь. Он может читать логи и предлагать причины; команды набирает человек.
- Всё необратимое на реальных данных. Восстановление поверх живой базы, удаления, изменения политики хранения. Это достаточно редко, чтобы выигрыш в скорости был небольшим, а возможный ущерб неограниченным.
- Проектирование контроля доступа. Кто до чего может дотянуться, политика firewall, роли IAM. Синтаксис агенты пишут хорошо, а политику плохо, обычно склоняясь к тому, что убирает ошибку.
- Обновления версий базовой инфраструктуры. Мажорные версии баз данных, изменения ядра и init. Знания агента слабее всего там, где заметки о выпуске новее всего.
Всё остальное допустимо, если на месте описанные ограничения.
FAQ#
Что лучше для DevOps: Claude Code или Cursor?
Оба справляются с одними и теми же основными задачами, и модель за ними важнее редактора. Для инфраструктуры важен контроль над выполнением команд: подтверждение перед запуском, правила разрешений и запретов, режим планирования только для чтения. Выбирайте тот, чьи средства контроля вы действительно оставите включёнными.
Можно ли позволить агенту заходить на мой сервер по SSH?
На одноразовую машину staging - да, с подтверждением команд по одной. На продакшен - нет. Изменение в продакшене должно быть проверенным коммитом, который выкатывает ваш пайплайн, а если агенту нужна информация из продакшена, человек может выполнить команду только для чтения и передать вывод.
Как не дать агенту выдумывать параметры конфигурации?
Не дать нельзя, можно только поймать. Сообщите агенту точные установленные версии, пусть после каждого изменения запускает собственный валидатор инструмента, а любой незнакомый ключ или флаг сверяйте с документацией для этой версии. Сильно помогает и вставка нужного раздела актуальной документации в сессию.
Безопасны ли Dockerfile, сгенерированные AI?
Типовые шаблоны обычно да, после проверки. Убедитесь, что базовый образ зафиксирован на реальном актуальном теге, что финальный этап работает от не-root пользователя, что секреты не копируются внутрь и не передаются как build-аргументы, и прогоните hadolint. Затем соберите образ и запустите в staging как любой другой.
Пусть агент пишет и тесты, и код?
Да, это одно из самых полезных его занятий. Но в каждом diff читайте изменения в тестах раньше изменений в коде, потому что агент, стремящийся к зелёному результату, может ослабить проверку или пропустить тест, а не исправить ошибку.


