MAATRIX / Блог / Telegram-бот падает после перезапуска: причины и решение

Telegram-бот падает после перезапуска: причины и решение

Telegram-бот падает после перезапуска: причины и решение

MAATRIX

Бот месяц работал, сервер перезагрузили — и тишина: команды не отвечают, апдейты копятся. Ситуация «telegram бот падает после перезапуска» сводится к одной из пяти причин, и каждая различается по логам за десять минут. Ниже — тексты ошибок, рабочий юнит systemd и проверка ребутом.

Обсудить статью, задать вопрос или начать новую тему

Есть вопрос по этой статье, идея для обсуждения или просто хотите поделиться опытом? Сообщество MAATRIX ждёт. Для общения, пожалуйста, зарегистрируйтесь в нашем личном кабинете.

Перейти в сообщество →

Три сценария «падает» и как их различить

Слово «падает» скрывает три поломки. Начните с трёх команд:

systemctl is-enabled tgbot; systemctl is-active tgbot
systemctl status tgbot --no-pager -l
journalctl -u tgbot -b --no-pager -n 100
  • disabled / inactive (dead) — бота никто не запускал: дело в автозапуске, а не в коде.
  • failed / activating (auto-restart) — стартует и умирает: в журнале трейсбек или Start request repeated too quickly.
  • active (running), но бот молчит — процесс жив, но апдейты не разбирает: Conflict или зависание.

Ловушка, из-за которой не видно причины: в Debian и Ubuntu журнал systemd лежит в tmpfs /run/log/journal и стирается при перезагрузке. Логи прошлой загрузки:

$ journalctl -u tgbot -b -1
Specifying boot ID or boot offset has no effect, no persistent journal was found.

Включите постоянный журнал до экспериментов:

sudo mkdir -p /var/log/journal
sudo systemd-tmpfiles --create --prefix /var/log/journal
sudo systemctl restart systemd-journald

В /etc/systemd/journald.conf поставьте Storage=persistent и SystemMaxUse=200M. И посмотрите last -x reboot | head -5: если там ребуты, которых вы не делали, дело в хосте, а не в боте.

Бот не стартует сам: tmux, nohup и забытый enable

Чаще всего бот не оформлен как сервис: его запустили руками в tmux, screen или через nohup python3 bot.py &. Всё это живёт до перезагрузки — после ребута pgrep -af bot.py пуст, хотя вручную бот стартует нормально.

Второй вариант — выполнили только systemctl start, забыв enable: симлинк в /etc/systemd/system/multi-user.target.wants/ не создан, при загрузке сервис никто не поднимет. Третий ловят реже: юнит положили в ~/.config/systemd/user/, а такие сервисы не стартуют при загрузке без linger (loginctl enable-linger bot). Проще системный /etc/systemd/system/tgbot.service:

[Unit]
Description=Telegram bot (aiogram)
Wants=network-online.target
After=network-online.target postgresql.service redis-server.service
StartLimitIntervalSec=300
StartLimitBurst=10

[Service]
Type=simple
User=bot
WorkingDirectory=/opt/tgbot
EnvironmentFile=/etc/tgbot/env
ExecStart=/opt/tgbot/.venv/bin/python -u /opt/tgbot/bot.py
Restart=always
RestartSec=5
KillSignal=SIGINT
MemoryMax=1G

[Install]
WantedBy=multi-user.target

Неочевидное: -u отключает буферизацию вывода Python, иначе логи идут в журнал пачками и трейсбек обрывается. KillSignal=SIGINT даёт боту поймать KeyboardInterrupt и закрыть сессию. StartLimitIntervalSec и StartLimitBurst обязаны быть в [Unit] — в [Service] systemd их молча проигнорирует. Дальше daemon-reload и systemctl enable --now tgbot. Для сервера с нуля есть настройка VPS под телеграм-ботов.

Развернуть за пару минут

Готовый образ на VPS MAATRIX: NVMe, AMD EPYC, root-доступ. Локации UK, США, Франция и РФ. Оплата картой РФ и по СБП.

Развернуть бот-стек

Юнит есть, но бот умирает за секунду

systemctl status показывает failed, а journalctl -u tgbot -b -n 50 — трейсбек. Четыре ошибки покрывают почти всё.

ModuleNotFoundError: No module named 'aiogram'. Библиотеки в venv, а в ExecStart системный /usr/bin/python3. source .venv/bin/activate в юните не сработает: активация — шелловская функция, systemd шелл не запускает. Указывайте интерпретатор напрямую: /opt/tgbot/.venv/bin/python.

error: externally-managed-environment. На Ubuntu 24.04 и Debian 13 системный pip защищён по PEP 668, а --break-system-packages ломает системный Python. Делайте venv и закрепляйте версии жёстко: aiogram==3.15.0, а не aiogram>=3 — иначе переустановка подтянет мажор с изменённым API.

FileNotFoundError: [Errno 2] No such file or directory: 'config.yaml'. У systemd рабочий каталог по умолчанию /, а не каталог проекта. Лечит строка WorkingDirectory=/opt/tgbot; надёжнее — абсолютные пути в коде.

KeyError: 'BOT_TOKEN'. Токен экспортирован в ~/.bashrc, а systemd эти файлы не читает. Выносите переменные в /etc/tgbot/env с правами 640 и владельцем root:bot. Деталь: это не shell-скрипт — export BOT_TOKEN=... не сработает, systemd примет export BOT_TOKEN за имя переменной и потеряет её. И не пишите Environment=BOT_TOKEN=... в юните: значение видно в systemctl show tgbot.

И про базу в /tmp: если SQLite лежит по пути /tmp/bot.db, после ребута будет sqlite3.OperationalError: unable to open database file или, хуже, чистая база — /tmp очищается при загрузке. Штатное место /var/lib/tgbot/ даёт StateDirectory=tgbot. После починки: systemctl reset-failed tgbot.

Conflict 409: два бота на один токен и застрявший вебхук

Странный случай: сервис active (running), процесс жив, а бот отвечает через раз. В журнале:

aiogram.exceptions.TelegramConflictError: Telegram server says - Conflict: terminated by
other getUpdates request; make sure that only one bot instance is running

Telegram отдаёт апдейты одному потребителю. Два процесса с getUpdates выбивают друг друга по очереди — снаружи это «одно сообщение бот увидел, второе нет». После ребута вылезает, когда старый экземпляр не остановился, а рядом стартовал новый. Ищите дубли везде:

pgrep -af "bot.py"
systemctl list-units --state=running | grep -i bot
sudo crontab -l | grep -i reboot
docker ps --format '{{.Names}}\t{{.Status}}'

Классика — юнит systemd плюс забытый @reboot python3 /opt/tgbot/bot.py в кроне: стартуют оба. Вторая — невыключенный старый VPS после переезда.

Разновидность 409 — застрявший вебхук: Conflict: can't use getUpdates method while webhook is active. Спросите Telegram: curl -s "https://api.telegram.org/bot$BOT_TOKEN/getWebhookInfo" | python3 -m json.tool. Ответ:

{"ok": true, "result": {
    "url": "https://old.example.com/hook",
    "pending_update_count": 1874,
    "last_error_message": "Connection refused"}}

Вебхук смотрит на мёртвый адрес, апдейты копятся, поллинг не работает. Снимаем вебхук вместе с очередью, чтобы бот не разгребал две тысячи старых сообщений: deleteWebhook?drop_pending_updates=true. Честное ограничение: Telegram сообщает факт конфликта, но не говорит, кто второй. Если дубль не находится, выход один — /revoke в @BotFather: старый экземпляр отвалится с Unauthorized.

Гонка при загрузке: сеть, DNS, база и сбитые часы

Бот стартует раньше, чем готова инфраструктура, падает и без Restart не поднимается. Признак: руками сервис стартует чисто, а после ребута — сетевой трейсбек.

DNS ещё не поднят: socket.gaierror: [Errno -3] Temporary failure in name resolution. After=network.target означает «сетевая подсистема инициализирована», а не «адрес получен и резолвер работает». Нужен network-online.target с обязательным Wants= плюс включённый systemd-networkd-wait-online.service (или NetworkManager-wait-online.service) — иначе target достигается мгновенно.

База не готова: connection to server at "127.0.0.1", port 5432 failed: Connection refused или отказ Redis на 6379. After=postgresql.service задаёт порядок запуска, но не гарантирует, что PostgreSQL принимает соединения. Ждать нужно явно:

ExecStartPre=/usr/bin/timeout 60 sh -c 'until pg_isready -h 127.0.0.1 -p 5432 -q; do sleep 2; done'

И After= работает только для сервисов этой же машины: про базу на соседнем сервере systemd не знает — спасают ретраи в коде и Restart=always.

Сбитые часы: ssl.SSLCertVerificationError: [SSL: CERTIFICATE_VERIFY_FAILED] certificate verify failed: certificate is not yet valid. Выглядит как проблема сертификата, а на деле у виртуалки после разворота из снапшота уехало время. В выводе timedatectl нужны System clock synchronized: yes и NTP service: active; если no и inactive — лечит timedatectl set-ntp true. Порядок старта покажет systemd-analyze critical-chain tgbot.service.

Тихая смерть: OOM-killer, диск и зависания

Отдельный класс — бот поднимается, работает час-другой и исчезает. Сначала память: journalctl -k -b -1 | grep -iE "out of memory|oom". Диагноз подтверждают:

Out of memory: Killed process 812 (python3) total-vm:1284412kB, anon-rss:412300kB
tgbot.service: Main process exited, code=killed, status=9/KILL
tgbot.service: Failed with result 'oom-kill'

Замерьте потребление вместо догадок: systemctl show tgbot -p MemoryCurrent вернёт что-то вроде MemoryCurrent=78643200, то есть 75 МБ. Пустой aiogram-бот на Python 3.12 в простое держит 60–90 МБ RSS; с pandas или обработкой фото счёт идёт на сотни мегабайт. На VPS с 512 МБ без swap такой бот вместе с PostgreSQL уезжает в OOM за часы — расчёт есть в статье про то, сколько ресурсов нужно VPS для телеграм-ботов. Страховка на минимум:

sudo fallocate -l 2G /swapfile && sudo chmod 600 /swapfile
sudo mkswap /swapfile && sudo swapon /swapfile
echo '/swapfile none swap sw 0 0' | sudo tee -a /etc/fstab

Честно: swap не лечит утечку, а оттягивает развязку и замедляет бота, живущего в свопе. Если RSS растёт линейно час за часом — это баг в коде. Зато MemoryMax=1G полезен всегда: ядро убьёт бота, а не sshd, и доступ к серверу останется.

Второй сценарий — забитый диск: OSError: [Errno 28] No space left on device, обычно из-за логов без ротации. Смотрите journalctl --disk-usage, чистите journalctl --vacuum-size=200M.

Третий — процесс жив, но ничего не делает: повисло соединение, зациклился воркер. Restart=always бесполезен, падения нет. Помогает watchdog: Type=notify и WatchdogSec=90 плюс sdnotify с вызовом notify("WATCHDOG=1") в главном цикле — минус честный, это правка кода. Дешевле проверка по таймеру: дёргает getMe, перезапускает юнит и шлёт алерт в Telegram.

В Docker политика перезапуска по умолчанию no, и после ребута контейнер не поднимется: docker inspect -f '{{.HostConfig.RestartPolicy.Name}}' tgbot, затем docker update --restart always tgbot. При unless-stopped контейнер, остановленный вручную до ребута, потом не стартует — by design. И проверяйте настоящим ребутом: systemctl is-enabled tgbot и systemctl is-active tgbot должны вернуть enabled и active, а journalctl -u tgbot -b — быть без трейсбеков.

Какой сервер нужен под живучий бот и как заказать в MAATRIX

Половина этих падений — не ошибки конфигурации, а нехватка ресурсов: OOM на 512 МБ, забитый диск, база и бот на одном ядре. Конфигурацию берите с запасом.

Профиль ботаМинимумКомфортно
Поллинг, без БД, до ~1000 пользователей1 vCPU / 1 ГБ / 20 ГБ NVMe2 vCPU / 2 ГБ / 40 ГБ NVMe
Бот + PostgreSQL + Redis, вебхук через nginx2 vCPU / 2 ГБ / 40 ГБ2 vCPU / 4 ГБ / 60–80 ГБ NVMe
Медиа, ffmpeg, очереди, обращения к ИИ-API2 vCPU / 4 ГБ4 vCPU / 8 ГБ / 100 ГБ NVMe

1 ГБ — минимум только для поллинг-бота без базы и медиа; добавьте PostgreSQL, и попадёте ровно в тот OOM, из-за которого читаете эту статью. 4 ГБ — точка, где бот, база, Redis и nginx живут спокойно, а swap остаётся страховкой. Диск берите не 20, а 40–80 ГБ: журнал, логи и бэкапы съедают место незаметно.

По локации для бот-стека советуем UK, Лондон: короткий и стабильный путь до api.telegram.org через европейскую точку — обычно десятки миллисекунд, чистые адреса без наследства прошлых арендаторов и привычная платёжным и ИИ-API юрисдикция. Российские провайдеры дают до Telegram API менее предсказуемое качество, а таймауты при поллинге снаружи выглядят ровно как «бот падает». RU оправдан при персональных данных россиян и 152-ФЗ: это осознанный размен на стабильность связи с API. Подробнее — про VPS в Великобритании для хостинга Telegram-бота.

Развернуть стек можно из панели MAATRIX за несколько минут: Ubuntu 24.04 LTS или Debian 13 и снапшот перед экспериментами с юнитом — чтобы откатиться, если ребут пойдёт не по плану. Оплата картами российских банков, по СБП, криптой или токеном MAAT.

Развернуть за пару минут

Готовый образ на VPS MAATRIX: NVMe, AMD EPYC, root-доступ. Локации UK, США, Франция и РФ. Оплата картой РФ и по СБП.

Развернуть бот-стек

Обсудить статью, задать вопрос или начать новую тему

Есть вопрос по этой статье, идея для обсуждения или просто хотите поделиться опытом? Сообщество MAATRIX ждёт. Для общения, пожалуйста, зарегистрируйтесь в нашем личном кабинете.

Перейти в сообщество →

Частые вопросы

Руками бот запускается, а после ребута нет?

Сервис не в автозагрузке: проверьте systemctl is-enabled tgbot. Если disabled, выполните sudo systemctl enable --now tgbot: start без enable автозапуск не создаёт.

Что означает Conflict: terminated by other getUpdates request?

Апдейты по одному токену опрашивают два процесса. Ищите дубль через pgrep -af bot.py, crontab -l | grep reboot и docker ps, а вебхук снимайте через deleteWebhook?drop_pending_updates=true.

Почему после перезагрузки нет логов за прошлую загрузку?

Журнал лежит в tmpfs и стирается при ребуте, поэтому journalctl -b -1 отвечает «no persistent journal was found». Создайте /var/log/journal, выполните systemd-tmpfiles --create --prefix /var/log/journal, перезапустите journald.

Нужны сами нейросети для контента?

Генерируйте изображения, видео и озвучку нейросетями на falapi.io — десятки моделей в одном окне. Оплата картой РФ и по СБП.