# Discord-бот на VPS: запуск через systemd с автоперезапуском

> Как поставить Python-бота на VPS, вынести токен в файл окружения и настроить systemd, чтобы бот поднимался после перезагрузки и сам перезапускался после падения.

- Рубрика: Гайды по играм
- Игра: VPS
- Опубликовано: 2026-11-13T14:40:00+03:00
- Обновлено: 2026-11-13T14:40:00+03:00
- Адрес: https://blog.egnetwork.ru/vps-discord-bot-systemd
- Автор: Команда EGN

Бот на домашнем компьютере работает, пока компьютер включён. Как только вы его выключили, сообщество остаётся без бота. VPS решает эту проблему: машина работает круглосуточно, а `systemd` поднимает бота после перезагрузки и перезапускает его, если процесс упал. Ниже — полная схема для Python-бота на Ubuntu 24.04.

## Коротко

Поставьте `discord.py` в виртуальное окружение, создайте в Developer Portal бота и включите в нём Message Content Intent. Токен положите в файл `.env` с правами 600, а службу опишите в `/etc/systemd/system/discord-bot.service` с `Restart=always`. Команда `systemctl enable --now discord-bot` запускает бота и включает автозапуск.

## Что понадобится

Нужен VPS с Ubuntu 24.04 или Debian 12, доступ по SSH по ключу (ключи SSH настраиваются до установки бота) и аккаунт Discord с приложением, созданным в [Discord Developer Portal](https://discord.com/developers/applications). Бот подключается к Discord сам, через исходящее WebSocket-соединение, поэтому входящий порт открывать не нужно.

![Схема: бот подключается к Discord исходящим соединением, systemd следит за процессом](https://blog.egnetwork.ru/media/613)

## Шаги

1. Создайте приложение в Developer Portal, на вкладке Bot нажмите «Reset Token» и скопируйте токен. Токен — это пароль бота: его нельзя публиковать и вставлять в код.
2. На той же вкладке Bot в разделе «Privileged Gateway Intents» включите **Message Content Intent**. Без него бот в текстовых каналах не увидит содержимое сообщений.
3. Установите Python-окружение и библиотеку:

```bash
sudo apt update && sudo apt install -y python3-venv
sudo useradd -r -s /usr/sbin/nologin botuser
sudo mkdir -p /opt/discord-bot && sudo chown botuser:botuser /opt/discord-bot
cd /opt/discord-bot
python3 -m venv venv
./venv/bin/pip install -U discord.py
```

4. Напишите `bot.py`. Токен читается из переменной окружения, в коде его нет:

```python
import os
import discord

intents = discord.Intents.default()
intents.message_content = True
client = discord.Client(intents=intents)

@client.event
async def on_ready():
    print(f"Бот подключён как {client.user}")

@client.event
async def on_message(message):
    if message.author == client.user:
        return
    if message.content == "!ping":
        await message.channel.send("pong")

client.run(os.environ["DISCORD_TOKEN"])
```

5. Создайте файл окружения `/opt/discord-bot/.env` (например, через `sudo nano`) с одной строкой:

```properties
DISCORD_TOKEN=вставьте-токен-сюда
```

Затем закройте его от чужих глаз. Токен без кавычек и пробелов вокруг `=`:

```bash
sudo chown botuser:botuser /opt/discord-bot/.env
sudo chmod 600 /opt/discord-bot/.env
```

Файл `bot.py` тоже должен принадлежать `botuser` или быть читаемым им: `sudo chown botuser:botuser /opt/discord-bot/bot.py`.

6. Опишите службу systemd в файле `/etc/systemd/system/discord-bot.service`:

```ini
[Unit]
Description=Discord bot
After=network-online.target
Wants=network-online.target

[Service]
User=botuser
WorkingDirectory=/opt/discord-bot
EnvironmentFile=/opt/discord-bot/.env
ExecStart=/opt/discord-bot/venv/bin/python bot.py
Restart=always
RestartSec=5

[Install]
WantedBy=multi-user.target
```

7. Примените настройки и запустите бота:

```bash
sudo systemctl daemon-reload
sudo systemctl enable --now discord-bot
sudo systemctl status discord-bot
```

8. Проверьте журнал. Логи бота попадают в `journalctl`, его можно смотреть в реальном времени:

```bash
sudo journalctl -u discord-bot -f
```

9. Пригласите бота на сервер через OAuth2 URL Generator в Developer Portal. Выберите scope `bot` и нужные права, например «Send Messages» и «Read Message History».

![Жизненный цикл службы: процесс упал, systemd ждёт пять секунд и поднимает его снова](https://blog.egnetwork.ru/media/611)

## Примеры

**Перезапуск после падения.** Имитация: ошибка в коде, процесс завершился с ненулевым кодом. Благодаря `Restart=always` systemd через `RestartSec=5` запускает бота заново. В журнале появятся две записи: остановка и новый старт.

**Перезагрузка VPS.** Команда `sudo reboot`. После загрузки служба стартует сама, потому что `enable` записал её в автозапуск. Проверьте командой `systemctl is-enabled discord-bot`, она должна вывести `enabled`.

**Обновление кода.** Положите новый `bot.py` в каталог, затем `sudo systemctl restart discord-bot`. Токен и окружение при этом не меняются.

**Вынос токена.** Чтобы сменить токен, отредактируйте `.env` и перезапустите службу. Если токен утёк, сбросьте его в Developer Portal, иначе старый продолжит работать у злоумышленника.

## Slash-команды вместо `!`-команд

Discord развивает slash-команды, и для них не нужен Message Content Intent. В discord.py 2.x они описываются через `app_commands`:

```python
from discord import app_commands

tree = app_commands.CommandTree(client)

@tree.command(name="ping", description="Проверка связи")
async def ping(interaction: discord.Interaction):
    await interaction.response.send_message("pong")

@client.event
async def on_ready():
    await tree.sync()
```

Команду нужно синхронизировать с Discord вызовом `tree.sync()`. Делайте это не при каждом запуске, если бот часто перезапускается: у синхронизации есть лимиты по частоте. Надёжнее вынести её в отдельную команду владельца или запускать один раз после изменения списка команд. Бота при этом нужно приглашать со scope `applications.commands` вместе с `bot`.

## Как посмотреть бота в кабинете EGN

Если бот работает на VPS EGN, к нему можно попасть по SSH и через «Консоль» в карточке сервера: она открывает терминал в браузере. Консоль полезна, когда ключ SSH остался на другом компьютере или закрыт порт 22. Откройте карточку сервера, нажмите «Консоль» и выполните те же команды, что и в SSH-клиенте.

![Верх карточки VPS: кнопка «Консоль» открывает терминал без SSH-клиента](https://blog.egnetwork.ru/media/612)

После входа полезны три команды:

```bash
sudo systemctl status discord-bot --no-pager
sudo journalctl -u discord-bot -n 100 --no-pager
sudo systemctl restart discord-bot
```

Первая показывает, работает ли служба и когда она стартовала. Вторая выводит последние сто строк журнала. Третья перезапускает бота после правки кода или `.env`.

## Симптом → причина → решение

| Симптом | Частая причина | Что сделать |
|---|---|---|
| `LoginFailure: Improper token` | Токен скопирован с ошибкой или сброшен | Сбросьте токен в Developer Portal и обновите `.env` |
| Бот онлайн, но команды не работают | Нет Message Content Intent или нет прав в канале | Включите intent и проверьте роль бота в настройках канала |
| `ModuleNotFoundError: No module named 'discord'` | Служба запускает не тот Python | В `ExecStart` укажите `/opt/discord-bot/venv/bin/python` |
| Служба перезапускается каждые 5 секунд | Ошибка в коде на старте | Смотрите `journalctl`, исправьте исключение, перезапустите |
| Бот не отвечает на slash-команды | Команды не синхронизированы с Discord | Вызовите `await tree.sync()` (см. раздел про slash-команды ниже) |
| После `reboot` бота нет | Нет `enable` | Выполните `sudo systemctl enable discord-bot` |

## Пример: ограничение прав через роли

Не давайте боту права администратора без необходимости. Если боту нужно только писать в канал, выдайте роли право «Отправка сообщений» только в нужном канале. Права выдаются в настройках сервера Discord, а не в коде. Бот с правами администратора может удалить каналы и роли, поэтому это первое, что стоит проверить после приглашения.

## Пример: команда с ответом

Добавьте в `bot.py` команду, которая отвечает текущим временем сервера. Это удобно для проверки часового пояса на VPS. Новый обработчик заменяет прежний `on_message`: в файле он должен быть один, поэтому объедините условия в одной функции:

```python
import datetime

@client.event
async def on_message(message):
    if message.author == client.user:
        return
    if message.content == "!time":
        now = datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S")
        await message.channel.send(f"Время на сервере: {now}")
```

Если ответ отличается от местного времени на несколько часов, проверьте часовой пояс VPS командой `timedatectl`.

## Частые ошибки

- **Бот в сети, но не отвечает на `!ping`.** Чаще всего не включён Message Content Intent. Включите его в Developer Portal и перезапустите службу.
- **`PrivilegedIntentsRequired`.** Discord не разрешает привилегированный intent, потому что он не включён в портале. Исправляется тем же шагом.
- **Токен в коде или в git.** Так токен утекает. Держите его в `.env` с правами 600 и в `.gitignore`.
- **Служба падает сразу после старта.** Проверьте путь в `ExecStart`: должен быть путь к Python внутри venv, а не системный `python3`. Журнал покажет ошибку импорта.
- **Бот не поднимается после перезагрузки.** Нет `enable`, или в `[Install]` не указан `WantedBy=multi-user.target`.
- **Служба перезапускается по кругу.** Если бот падает при каждом старте, `Restart=always` будет поднимать его снова и снова с паузой `RestartSec=5`. Это не лечит причину: найдите исключение в журнале. Пока бот падает, службу можно остановить: `sudo systemctl stop discord-bot`.

## Итог

Бот на VPS работает круглосуточно, если у него есть три вещи: окружение Python с `discord.py`, токен в файле с правами 600 и служба systemd с `Restart=always`. Включённый Message Content Intent и журнал `journalctl` решают большинство проблем. Если рядом понадобятся сайт или база данных для статистики бота, посмотрите статьи [Сайт на VPS с nginx и Let's Encrypt](https://blog.egnetwork.ru/vps-sait-nginx-letsencrypt) и [Docker Compose на VPS для нескольких сервисов](https://blog.egnetwork.ru/vps-docker-compose-neskolko-servisov).

## Чек-лист после запуска

Пройдите этот список один раз, прежде чем считать бота готовым. Он занимает десять минут и экономит вечер в поддержке.

1. Служба `enabled` и `active (running)` в `systemctl status`.
2. После `sudo reboot` бот снова онлайн без ручного входа.
3. Токен нигде не записан в открытом виде: ни в коде, ни в истории команд, ни в git.
4. Файл `.env` имеет права 600 и владельца `botuser`.
5. В журнале нет повторяющихся ошибок за последний час: `journalctl -u discord-bot --since "1 hour ago"`.
6. Бот не имеет прав администратора, если они не нужны для работы.
7. На VPS открыт только SSH, а бот не слушает входящие порты. Это можно проверить командой `sudo ss -tulpn`.

Если пункт не выполняется, исправьте его до приглашения бота на большой сервер. Ошибка в правах или токене на тестовом сервере обходится дешевле, чем на сообществе из тысячи участников.

## Как обновить код без простоя

Простой на время перезапуска короткий: `systemctl restart` занимает секунды. Но если бот обрабатывает длинные задачи, сначала дождитесь их завершения. Порядок такой: обновите файлы в каталоге, проверьте синтаксис командой `./venv/bin/python -m py_compile bot.py`, затем перезапустите службу и проверьте журнал. Если ошибка, верните предыдущую версию файла из git или из резервной копии каталога.

## Вопросы и ответы

### Нужен ли мощный VPS для Discord-бота?
Для небольшого бота хватит самого простого тарифа: процесс на discord.py обычно занимает десятки мегабайт памяти. Больше ресурсов нужно, если бот работает с медиа, базой данных или большим числом серверов Discord. Реальное потребление смотрите командой `htop` или в «Мониторинге» карточки VPS.

### Можно ли запускать бота на Node.js?
Да, в Discord есть библиотека discord.js. Схема та же: окружение Node, токен в `.env`, служба systemd с `Restart=always`.

### Зачем отдельный пользователь botuser?
Бот не должен работать от root. Отдельный пользователь без shell ограничивает ущерб, если в коде найдётся уязвимость.

### Что делать, если токен утёк?
Сбросьте его во вкладке Bot в Developer Portal, обновите `.env` и перезапустите службу. Старый токен после сброса перестаёт работать.

Сервер под игры за пару минут — [egnetwork.ru](https://egnetwork.ru/)

## Официальные источники

- [Discord Developer Portal](https://discord.com/developers/applications)

---

Материал EGN (blog.egnetwork.ru), обновлено 13 ноября 2026; при цитировании указывайте ссылку.
