Telegram-бот на Linux VPS: systemd или Docker, логи, автозапуск и резервное копирование
Telegram-бот может нормально работать на ноутбуке разработчика во время тестов, но для постоянной эксплуатации этого недостаточно. Ноутбук выключается, соединение пропадает, процесс завершается после закрытия терминала — и бот перестает отвечать.
На Linux VPS приложение можно запустить как обычный сервис: оно будет стартовать вместе с сервером, автоматически восстанавливаться после части сбоев, писать логи и хранить рабочие данные независимо от компьютера разработчика.
При этом сам факт размещения на VPS еще не делает бота надежным. Нужно решить, где хранить токен, как запускать приложение после перезагрузки, где смотреть ошибки, что резервировать и как обновлять код так, чтобы после очередного релиза не обнаружить неработающий процесс.
В этой инструкции не будем писать Telegram-бота с нуля. Будем считать, что приложение уже готово и локально запускается. Задача — превратить его в сервис, рассчитанный на работу 24/7.
Что подготовить перед запуском Telegram-бота на VPS
Для примеров будем использовать условного Python-бота, расположенного в каталоге /opt/telegram-bot.
Точка запуска:
/opt/telegram-bot/bot.pyЕсли бот написан на Node.js, Go или другом языке, общая схема не меняется: отличаются только runtime, зависимости и команда запуска.
Перед настройкой должны быть готовы:
- Linux VPS;
- SSH-доступ;
- отдельный пользователь для приложения;
- runtime нужной версии;
- код бота;
- зависимости;
- токен Telegram-бота;
- место для постоянных данных;
- понимание, использует бот long polling или webhook.
Рабочий токен нельзя публиковать в Git, вставлять в статьи, скриншоты или оставлять в общедоступных логах.
Также заранее определите, какие данные существуют только на VPS. Например:
- SQLite-база;
- файлы пользователей;
- локальные документы;
- настройки;
- очередь задач;
- данные о заказах или статусах;
- другое состояние, которое приложение создает уже во время работы.
Именно эти данные впоследствии определят схему резервного копирования.
Long polling или webhook: что выбрать для VPS
Telegram поддерживает два основных способа получать новые события:
- long polling через
getUpdates; - webhook.
Для одного бота одновременно использовать оба варианта нельзя. Если установлен webhook, получение обновлений через getUpdates недоступно.
Long polling
При long polling приложение само обращается к Telegram и ожидает новые обновления.
Telegram API
▲
│ getUpdates
│
Telegram-бот на VPSДля небольшого бота на одном VPS этот вариант часто проще:
- не нужен публичный HTTP endpoint;
- не требуется отдельный домен только ради Telegram;
- не нужно настраивать reverse proxy специально для webhook;
- процесс удобно контролировать через systemd или Docker.
Именно long polling будем считать основным сценарием этой статьи.
Webhook
При webhook направление меняется: Telegram отправляет HTTPS-запросы на адрес приложения.
Telegram
│ HTTPS POST
▼
домен / reverse proxy
│
▼
Telegram-ботWebhook удобен, если у проекта уже есть HTTP-приложение, домен, HTTPS и reverse proxy.
Для дополнительной проверки запросов можно использовать секрет webhook, который Telegram передает в специальном HTTP-заголовке.
Есть еще один важный нюанс для работы 24/7: Telegram не хранит неполученные обновления бесконечно. Если бот долго был выключен, нельзя рассчитывать, что после запуска он обязательно обработает всю накопившуюся очередь.
Поэтому автоматический перезапуск и контроль состояния нужны не только ради удобства администратора.
Запуск Telegram-бота через systemd
Для одного небольшого бота без контейнерной инфраструктуры systemd обычно самый прямой вариант.
Telegram
│
▼
приложение бота
│
▼
systemd
├─ запуск при старте VPS
├─ перезапуск после сбоя
└─ логи через journaldСоздаем отдельного пользователя
Постоянно запускать приложение от root не нужно.
Например, создадим системного пользователя:
sudo useradd --system --home /opt/telegram-bot --shell /usr/sbin/nologin telegrambotЕсли каталог приложения уже существует, передадим его этому пользователю:
sudo chown -R telegrambot:telegrambot /opt/telegram-botПодготавливаем виртуальное окружение
Для Python-приложения можно использовать отдельное virtual environment:
cd /opt/telegram-bot
python3 -m venv .venvЗатем установим зависимости:
/opt/telegram-bot/.venv/bin/pip install -r requirements.txtПосле установки полезно проверить приложение вручную под тем же пользователем, от которого впоследствии будет работать сервис:
sudo -u telegrambot /opt/telegram-bot/.venv/bin/python /opt/telegram-bot/bot.pyЕсли бот уже на этом этапе завершается с ошибкой, автоматический перезапуск пока не нужен. Сначала добейтесь нормального ручного запуска.
Где хранить токен
Плохой вариант — записать рабочий токен непосредственно в исходный код:
TOKEN = "123456789:REAL_BOT_TOKEN"После попадания такого файла в Git секрет может сохраниться в истории даже после удаления строки из актуальной версии.
Для простого systemd-сервиса токен можно вынести в отдельный файл, который не хранится в репозитории.
Например:
/etc/telegram-bot.envСодержимое файла:
BOT_TOKEN=replace_with_real_tokenОграничим права:
sudo chown root:root /etc/telegram-bot.env
sudo chmod 600 /etc/telegram-bot.envПриложение при этом должно получать токен из переменной окружения, а не из исходного файла проекта.
Создаем systemd unit
Создадим файл:
/etc/systemd/system/telegram-bot.serviceПример содержимого:
[Unit]
Description=Telegram Bot
After=network-online.target
Wants=network-online.target
[Service]
Type=simple
User=telegrambot
Group=telegrambot
WorkingDirectory=/opt/telegram-bot
EnvironmentFile=/etc/telegram-bot.env
ExecStart=/opt/telegram-bot/.venv/bin/python /opt/telegram-bot/bot.py
Restart=on-failure
RestartSec=5
[Install]
WantedBy=multi-user.targetОсновные параметры:
User=— пользователь, от которого работает процесс;WorkingDirectory=— рабочий каталог;EnvironmentFile=— файл с переменными;ExecStart=— команда запуска;Restart=on-failure— перезапуск после аварийного завершения;RestartSec=5— пауза перед новой попыткой запуска.
Путь в ExecStart нельзя копировать вслепую. Если виртуальное окружение находится в другом каталоге или файл запуска называется иначе, unit нужно изменить.
После создания unit перечитаем конфигурацию systemd:
sudo systemctl daemon-reloadЗапустим бота:
sudo systemctl start telegram-botПроверим состояние:
sudo systemctl status telegram-botЕсли сервис работает правильно, включим запуск вместе с системой:
sudo systemctl enable telegram-botИли сразу включим автозапуск и запустим сервис:
sudo systemctl enable --now telegram-botСмотрим логи systemd
Последние записи сервиса:
sudo journalctl -u telegram-bot -n 100Просмотр в реальном времени:
sudo journalctl -u telegram-bot -fТолько записи после текущей загрузки VPS:
sudo journalctl -u telegram-bot -bВ журнале обычно можно увидеть:
- ошибку импорта модуля;
- неверный путь в
ExecStart; - отсутствие файла конфигурации;
- ошибку подключения к БД;
- проблему доступа к Telegram API;
- исключение приложения;
- отказ в доступе к файлу.
Важно различать два состояния:
systemd показывает failed
│
└─ процесс завершился или не запустился
systemd показывает active
│
└─ процесс жив, но бот все равно может не отвечатьВо втором случае нужно диагностировать уже само приложение, сеть, Telegram API и способ получения обновлений.
Запуск Telegram-бота через Docker Compose
Если приложение уже контейнеризировано или вместе с ботом работают PostgreSQL, Redis и другие сервисы, Docker Compose часто удобнее отдельного systemd-unit для каждого процесса.
Docker Compose
│
├─ bot
├─ база / Redis при необходимости
├─ persistent data
└─ secretsПолный production-процесс Docker мы уже разбирали отдельно, поэтому здесь сосредоточимся только на особенностях Telegram-бота.
Минимальный Dockerfile
Для условного Python-приложения:
FROM python:3.13-slim
WORKDIR /app
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt
COPY . .
CMD ["python", "bot.py"]Версию Python нужно выбирать по требованиям конкретного проекта, а не по примеру в статье.
Compose-файл
services:
bot:
build: .
restart: unless-stopped
secrets:
- bot_token
volumes:
- ./data:/app/data
secrets:
bot_token:
file: ./secrets/bot_tokenДля постоянно работающего бота политика unless-stopped дает понятную логику: контейнер должен запускаться автоматически, пока администратор явно не остановил его вручную.
Как приложение получает secret
Compose secrets предоставляются контейнеру как файлы в каталоге:
/run/secrets/В нашем примере токен будет доступен по пути:
/run/secrets/bot_tokenПриложение должно уметь прочитать значение из этого файла.
Сам файл секрета на VPS тоже нужно защитить:
chmod 600 ./secrets/bot_tokenИ исключить секреты из Git. Например:
secrets/
.env
data/Последнюю строку нужно применять осознанно. Если внутри data/ находятся шаблонные файлы или структура каталогов, которая должна храниться в репозитории, правило лучше сделать точнее.
Запускаем контейнер
docker compose up -dПроверяем состояние:
docker compose psСмотрим логи:
docker compose logs botИли в реальном времени:
docker compose logs -f botЕсли контейнер имеет статус Up, это означает только, что основной процесс пока не завершился. Сам бот все равно может потерять доступ к базе, внешнему API или перестать корректно обрабатывать updates.
Диагностика, безопасность и обновление Telegram-бота
Что делать после утечки токена
Независимо от способа запуска не стоит:
- записывать рабочий токен прямо в исходный код;
- коммитить
.envили файл секрета в Git; - вставлять токен в
compose.yaml; - публиковать его на скриншотах;
- выводить токен целиком в debug-логи;
- отправлять его вместе с открытым архивом проекта.
Если рабочий токен оказался раскрыт, безопаснее считать его скомпрометированным и заменить через BotFather. Простого удаления из текущего файла недостаточно.
Почему бот перестал отвечать
Когда пользователь сообщает, что «бот не работает», сначала нужно определить уровень проблемы.
VPS доступен?
│
▼
процесс бота запущен?
│
▼
есть ли ошибки в логах?
│
▼
доступен Telegram API?
│
▼
получает ли бот updates?
│
▼
работает ли БД / внешние API?Для systemd:
sudo systemctl status telegram-bot
sudo journalctl -u telegram-bot -n 100Для Docker:
docker compose ps
docker compose logs --tail=100 botПроверяем, не остался ли webhook
Если приложение перевели на long polling, а для этого же бота ранее был установлен webhook, получение обновлений через getUpdates работать не будет.
Текущее состояние webhook можно проверить штатным методом getWebhookInfo. При переводе обратно на long polling webhook удаляется методом deleteWebhook.
Проверяем, не запущены ли два экземпляра
После миграции иногда получается такая схема:
старый VPS ──┐
├── один Telegram-бот
новый VPS ───┘Оба процесса используют один токен и пытаются работать с одной очередью updates.
При переносе недостаточно запустить приложение на новом VPS — нужно также убедиться, что старый экземпляр действительно остановлен.
Проверяем место на диске
df -hЕсли файловая система заполнена, приложение может перестать писать SQLite, создавать временные файлы или сохранять логи.
Также стоит проверить inode:
df -iСвободные гигабайты не помогут, если приложение создало огромное количество мелких файлов и исчерпало доступные inode.
Как безопасно обновлять Telegram-бота
Обновление production-бота лучше воспринимать как небольшой runbook, а не как одну команду git pull.
Перед обновлением:
- Проверьте текущее состояние.
- Убедитесь, что резервная копия рабочих данных актуальна.
- Зафиксируйте текущую рабочую версию.
- Получите новую версию кода или образа.
- Обновите зависимости.
- Перезапустите сервис.
- Посмотрите логи.
- Отправьте тестовую команду боту.
- Проверьте критичные сценарии.
- При ошибке верните предыдущую рабочую версию.
Обновление systemd-варианта
После обновления кода и зависимостей:
sudo systemctl restart telegram-botСразу после перезапуска:
sudo systemctl status telegram-bot
sudo journalctl -u telegram-bot -n 100Если изменялся сам unit-файл:
sudo systemctl daemon-reload
sudo systemctl restart telegram-botОбновление Docker-варианта
Если образ собирается локально:
docker compose build
docker compose up -dЕсли образ приходит из registry:
docker compose pull
docker compose up -dПосле обновления:
docker compose ps
docker compose logs --tail=100 botВажно отличать это от простой команды:
docker compose restartdocker compose restart только перезапускает существующий контейнер. Если изменился compose.yaml, конфигурацию обычно нужно применить через docker compose up -d, чтобы Compose при необходимости пересоздал контейнер.
Резервное копирование и проверка после перезагрузки
Git-репозиторий с исходным кодом не является полной резервной копией рабочего бота.
Нужно определить, какое состояние создается уже после запуска приложения.
SQLite
Если бот хранит данные в SQLite, база может находиться, например, в:
/opt/telegram-bot/data/bot.dbПростое копирование активно изменяемого файла не всегда дает согласованную резервную копию. Лучше использовать штатный механизм backup конкретной СУБД или корректно остановить запись на время операции.
PostgreSQL или MySQL
Для отдельной серверной СУБД нужны собственные дампы или другая согласованная схема резервного копирования.
Копия исходного кода и резервная копия базы решают разные задачи.
Пользовательские и рабочие файлы
Бот может сохранять:
- документы;
- фотографии;
- CSV/XLSX;
- счета;
- отчеты;
- медиаданные;
- файлы интеграций.
Если эти данные нужны приложению позже, каталог должен попадать в backup.
Конфигурация сервиса
Полезно сохранять:
- systemd unit;
compose.yaml;- Dockerfile;
- версии зависимостей;
- структуру необходимых каталогов;
- инструкцию восстановления.
При этом резервная копия конфигурации не должна случайно превращаться в открытый архив со всеми секретами.
Проверяем восстановление
Бэкап, который ни разу не проверяли на восстановление, дает ложное чувство безопасности.
создали backup
│
▼
восстановили в тестовую среду
│
▼
запустили приложение
│
▼
проверили данныеДля рабочего бота такой тест особенно полезен перед крупным обновлением приложения или схемы базы данных.
Проверяем работу после перезагрузки VPS
Настроенный сервис должен переживать обычный reboot без ручного входа администратора.
Проверить это лучше намеренно в подходящее техническое окно.
Перед перезагрузкой для systemd:
sudo systemctl status telegram-botДля Docker:
docker compose psЗатем перезагрузите VPS:
sudo rebootПосле восстановления SSH снова проверьте сервис.
Для systemd:
sudo systemctl status telegram-botДля Docker:
docker compose psПосле этого:
- отправьте тестовое сообщение;
- проверьте базу данных;
- посмотрите свежие логи;
- убедитесь, что рабочие файлы на месте;
- проверьте свободное место;
- проверьте внешние API, если бот от них зависит.
Systemd или Docker Compose: что выбрать
| Критерий | systemd | Docker Compose |
|---|---|---|
| Один небольшой бот | Обычно проще | Может быть избыточен |
| Изоляция окружения | Зависит от настройки ОС | Выше за счет контейнера |
| Приложение уже контейнеризировано | Дополнительный слой обычно не нужен | Естественный выбор |
| Логи | journalctl | docker compose logs |
| Автозапуск | systemd unit | restart policy + Docker daemon |
| Несколько связанных сервисов | Можно настроить отдельно | Обычно удобнее описать вместе |
| Перенос окружения | Нужно воспроизводить настройки хоста | Контейнерную среду обычно проще повторить |
Если бот — один Python-процесс с небольшой SQLite-базой, systemd зачастую дает все необходимое без дополнительной контейнерной инфраструктуры.
Если приложение уже собирается в Docker или рядом работают PostgreSQL, Redis, worker и другие сервисы, Compose обычно проще поддерживать как единую конфигурацию.
Нет смысла переводить стабильный systemd-сервис в Docker только ради самого факта использования контейнеров. И наоборот, контейнеризированное приложение не стоит вручную распаковывать на хост только ради systemd.
Заключение
Разместить Telegram-бота на Linux VPS несложно: для первого запуска иногда достаточно одной команды. Гораздо важнее сделать так, чтобы спустя месяц не требовалось вспоминать, из какого каталога он запускался и почему перестал отвечать после перезагрузки.
Для одного небольшого приложения обычно достаточно systemd: отдельный пользователь, корректный ExecStart, автозапуск, restart и журнал systemd.
Docker Compose удобнее, когда приложение уже контейнеризировано или состоит из нескольких связанных сервисов. В этом случае отдельно нужно продумать volumes, secrets и порядок обновления контейнеров.
В обоих вариантах остаются общие задачи: защита токена, сохранение рабочих данных, проверка логов, резервное копирование и реальный тест восстановления после reboot.
Что почитать дальше
- Первичная настройка Ubuntu VPS после заказа — SSH, обновления, firewall и базовая подготовка сервера.
- Docker на Linux VPS: production-развертывание — сеть, тома, HTTPS, обновления и резервное копирование контейнеров.
- Как подключиться к Linux VPS по SSH — ключи, fingerprint, нестандартный порт и диагностика удаленного доступа.
Когда стоит обратиться к специалистам
С одним небольшим ботом без внешних зависимостей обычно можно справиться самостоятельно.
Сложность заметно растет, если бот связан с рабочей базой данных, платежами, CRM, очередями, несколькими worker-процессами или другими сервисами. В этом случае простой автоперезапуск не заменяет нормальную схему эксплуатации.
Для такого сценария можно подобрать Linux VPS IPWAY под само приложение, базу данных и рабочую нагрузку, оставив небольшой запас ресурсов для обновлений и роста.
Кратко
- Для работы 24/7 Telegram-бот должен запускаться как сервис, а не из открытого SSH-терминала.
- Для одного простого бота systemd обычно проще, для контейнеризированного приложения или нескольких сервисов удобнее Docker Compose.
- Токен Telegram нельзя хранить в исходном коде и публичном Git-репозитории.
- Long polling и webhook нельзя одновременно использовать как два способа получения updates одного бота.
- Автоматический restart восстанавливает процесс, но не исправляет ошибки приложения.
- Логи нужно проверять даже тогда, когда systemd или Docker показывают работающий процесс.
- Git не заменяет резервную копию базы данных и пользовательских файлов.
- После настройки обязательно проверьте реальный reboot VPS и функциональный ответ бота.