Переезд бота на VPS: чек-лист, после которого он живёт без тебя
Бот на ноутбуке работает, пока ты рядом: сам видишь трейсбек, сам перезапускаешь. На сервере этого человека нет — его обязанности приходится раздать машине. Разбираю по шагам, что именно нужно настроить.
Ноутбук закрыл — бот умер. Пока ты сидишь рядом, мониторинг — это ты сам: увидел трейсбек в терминале, руками поднял упавший процесс, подложил новый токен. На VPS такого человека нет. Все его обязанности придётся раздать машине, по пунктам, ничего не пропуская.
Порядок ниже собрался сам собой, когда я переносил на сервер бота для проверки вывесок, меню и рекламы на соответствие 168-ФЗ о русском языке. Стек обычный: aiogram 3, httpx, SQLite через aiosqlite, конфиг на pydantic-settings.
Шаг 1. Зафиксировать зависимости и конфиг
«У меня локально работало» — самая частая поломка при переезде. Лечится двумя файлами.
requirements.txt — с версиями. У меня это нижние границы вида aiogram>=3.20, openai>=1.30, aiosqlite>=0.20. Удобно, пока обновляешься сам. Нужна полная предсказуемость на сервере — фиксируй точные версии через pip freeze.
.env.example — пустой шаблон со всеми переменными, лежит в репозитории. Реальный .env с токенами в git не попадает никогда. На сервере копируешь шаблон и заполняешь:
cp .env.example .env
Читать переменные лучше через pydantic-settings. Забыл ключ — бот падает на старте с внятным сообщением. Альтернатива: None всплывёт где-то в середине обработчика через два дня.
Шаг 2. Докер вместо «поставил питон руками»
Вручную поставленный Python на сервере — отдельная сущность, которую надо помнить и обслуживать. Проще упаковать всё в контейнер:
docker compose up -d --build
docker compose logs -f
Одна строчка в docker-compose.yml делает половину работы за тебя:
restart: unless-stopped
Бот сам поднимется после падения, после docker restart и после перезагрузки сервера. Первая обязанность с тебя снята.
Шаг 3. Данные должны пережить пересборку
База SQLite внутри контейнера умрёт при следующем --build вместе со всей историей пользователей. Выноси файл базы в volume:
volumes:
- ./data:/app/data
И сразу подумай про бэкап. Для SQLite хватит cron-задачи, которая раз в сутки копирует файл базы в отдельную папку. Копируй через sqlite3 base.db ".backup copy.db" — обычный cp может поймать базу в момент записи.
Шаг 4. Один бот — один процесс
На long polling два запущенных экземпляра начинают драться за getUpdates. Симптом узнаваемый: сообщения приходят через раз, в логах — конфликты. Перед запуском на сервере заглуши локальную копию и убедись, что старый контейнер остановлен.
Отдельная история — тестовый бот. Заведи для разработки второй токен у @BotFather, чтобы не дёргать боевой.
Шаг 5. Логи, которые можно прочитать через неделю
docker compose logs -f хорош, пока ты смотришь в него прямо сейчас. Через месяц без ограничений логи спокойно съедают диск, и бот падает по самой глупой из возможных причин. Ограничение ставится там же, в compose:
logging:
driver: json-file
options:
max-size: "10m"
max-file: "3"
В коде логируй сам факт старта: какая модель, какая версия конфига. Ошибки и так попадут в лог. Когда бот начнёт вести себя странно, первый вопрос будет «а что вообще запущено».
Шаг 6. Веб-часть и nginx, если она есть
Если к боту прилагается вебхук или админка на FastAPI с uvicorn, наружу их выставляет nginx. Конфиг живёт в /etc/nginx/sites-enabled/, правится руками через SSH и после каждой правки проверяется:
nginx -t && systemctl reload nginx
Сертификат — certbot, он же сам продлевает. Telegram принимает вебхуки только по HTTPS, так что шаг обязательный.
Шаг 7. Деплой без ручного SSH
Последним стоит автоматизировать сам выкат. У меня статика лендинга уезжает на сервер по push в master, и это заметно меняет ощущение от работы: правки перестают откладываться «до вечера, когда сяду за сервер».
Для бота схема та же. GitHub Actions по пушу заходит на VPS и выполняет git pull && docker compose up -d --build. Секреты живут в настройках репозитория и в .env на сервере.
Короткий чек-лист перед тем, как закрыть ноутбук
.envзаполнен на сервере, в git его нетrestart: unless-stoppedпрописан- база вынесена в volume, бэкап настроен
- логи ограничены по размеру
- локальная копия бота выключена
- бот отвечает на
/startс телефона, из мобильной сети - ты знаешь команду, которой посмотришь логи через две недели
Последний пункт важнее, чем кажется. Запустить — это полдела. Переезд удался, когда бот работает неделями, а ты вспоминаешь о нём только по своему желанию.
Автоматизация для бизнеса
Вторая сторона исследования: собираю под бизнес ИИ-агентов и автоматизации, которые работают и с клиентами, и с командой. Система остаётся у вас в работе и под вашим управлением.