Хостинг Discord-бота на VPS: держим бота онлайн 24/7
- 18 октября 2026 г.
- 09:00
- Автор: Adrien Roche
- Уроки

Почему именно на VPS Discord-боты реально остаются онлайн
Discord-бот не веб-страница. После входа он держит постоянное WebSocket-соединение с gateway Discord и обязан непрерывно отвечать на heartbeat; как только процесс останавливается, бот показывается офлайн и все команды перестают работать. Одно это требование (процесс, который никогда не спит) отсекает почти весь «бесплатный» хостинг:
- Heroku убрал бесплатные dyno в ноябре 2022 года.
- Replit больше не держит бесплатные проекты запущенными: режим always-on перешёл в платные развёртывания.
- Glitch закрыл хостинг приложений в 2025 году.
- Бесплатные веб-тарифы, которые засыпают при простое, рвут соединение с gateway при каждом простое; для пользователей такой бот просто не работает.
Хостинг Discord-бота на VPS решает проблему в корне: круглосуточно работающая Linux-машина с root-доступом, где бот работает как обычная служба под управлением системы инициализации. В этом руководстве всё собрано на Ubuntu 24.04 (подходит также для 22.04 и Debian 12) с discord.py и discord.js, а дальше разобраны работа с токеном, перезапуски, логи, обновления и память.
Подготовка VPS: отдельный пользователь и среда выполнения
Сначала закройте входную дверь: включите SSH на Ubuntu и укрепите его (аутентификация по ключу, запрет входа под root и ufw). Бот подключается к Discord исходящим соединением и не требует входящих портов, поэтому файрвол может быть закрыт для всего, кроме SSH.
Шаг 1: создайте непривилегированную учётную запись для бота
sudo useradd -r -m -d /opt/mybot -s /usr/sbin/nologin bot
Отдельная учётная запись означает, что забагованный бот или скомпрометированная зависимость из npm либо PyPI не сможет прочитать ваши SSH-ключи или задеть другие службы. Всё лежит в /opt/mybot и принадлежит пользователю bot.
Шаг 2 (Python): установите discord.py в виртуальное окружение
sudo apt update && sudo apt install -y python3 python3-venv python3-pip
sudo -u bot python3 -m venv /opt/mybot/.venv
sudo -u bot /opt/mybot/.venv/bin/pip install -U discord.py python-dotenv
discord.py 2.x требует Python 3.8 или новее; в Ubuntu 24.04 идёт 3.12, в 22.04 идёт 3.10, так что системного интерпретатора достаточно. Добавляйте discord.py[voice] и ffmpeg только ради голосовых каналов.
Шаг 3 (Node.js): установите актуальный LTS и discord.js
curl -fsSL https://deb.nodesource.com/setup_22.x | sudo -E bash -
sudo apt install -y nodejs
sudo -u bot bash -c 'cd /opt/mybot && npm init -y && npm install discord.js dotenv'
Пакет nodejs из репозитория Ubuntu сильно отстаёт от upstream, а discord.js v14 требует современной среды выполнения, поэтому берите актуальный LTS через NodeSource (выше) или nvm. При использовании nvm выполните which node и укажите этот абсолютный путь в юните службы.
Держите токен вне кода: файл .env
Токен бота равносилен паролю с полным контролем над ботом: никогда не вставляйте его в bot.py или index.js и не коммитьте. Положите его в /opt/mybot/.env, доступный на чтение только учётной записи бота, и используйте редактор, а не echo: токен, набранный в командной строке, попадёт в историю оболочки:
sudo -u bot nano /opt/mybot/.env # one line: DISCORD_TOKEN=your-token-here
sudo chmod 600 /opt/mybot/.env
echo ".env" | sudo -u bot tee -a /opt/mybot/.gitignore
Формат: по одной строке KEY=value, без кавычек и без export. Его читают python-dotenv и npm-пакет dotenv, а systemd может подставить тот же файл через EnvironmentFile= (следующий раздел); сочетать оба способа безвредно, поскольку ни одна из библиотек не перезаписывает уже существующую переменную. Если токен утёк, немедленно сбросьте его в Developer Portal в разделе Bot → Reset Token.
Минимальный бот на discord.py и discord.js
Оба примера читают токен из окружения, включают intent Message Content и отвечают на !ping. В Developer Portal создайте приложение, добавьте бота, скопируйте токен (показывается один раз), включите Message Content Intent в разделе Bot → Privileged Gateway Intents и пригласите бота на тестовый сервер через генератор OAuth2 URL со scope bot.
discord.py: /opt/mybot/bot.py
import os
import discord
from dotenv import load_dotenv
load_dotenv()
intents = discord.Intents.default()
intents.message_content = True
client = discord.Client(intents=intents)
@client.event
async def on_ready():
print(f"Logged in as {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"])
discord.js: /opt/mybot/index.js
require("dotenv").config();
const { Client, GatewayIntentBits, Events } = require("discord.js");
const client = new Client({
intents: [
GatewayIntentBits.Guilds,
GatewayIntentBits.GuildMessages,
GatewayIntentBits.MessageContent,
],
});
client.once(Events.ClientReady, (c) => {
console.log(`Logged in as ${c.user.tag}`);
});
client.on(Events.MessageCreate, (message) => {
if (message.author.bot) return;
if (message.content === "!ping") message.reply("pong");
});
client.login(process.env.DISCORD_TOKEN);
Запустите его один раз в переднем плане: sudo -u bot /opt/mybot/.venv/bin/python /opt/mybot/bot.py или sudo -u bot node /opt/mybot/index.js, дождитесь Logged in as, отправьте !ping и нажмите Ctrl-C. Пока бот живёт ровно столько, сколько ваша SSH-сессия: при закрытии терминала дочерние процессы получают SIGHUP и бот умирает.
Запуск под systemd с Restart=always
systemd и так является системой инициализации Ubuntu, поэтому держать бота онлайн после выхода из сессии, сбоя и перезагрузки аккуратнее всего через юнит службы: никакого лишнего демона, а логи попадают в journal.
Шаг 1: напишите юнит
sudo nano /etc/systemd/system/mybot.service
[Unit]
Description=Discord bot
After=network-online.target
Wants=network-online.target
[Service]
Type=simple
User=bot
Group=bot
WorkingDirectory=/opt/mybot
EnvironmentFile=/opt/mybot/.env
Environment=PYTHONUNBUFFERED=1
ExecStart=/opt/mybot/.venv/bin/python /opt/mybot/bot.py
Restart=always
RestartSec=5
NoNewPrivileges=true
PrivateTmp=true
ProtectSystem=strict
ProtectHome=true
ReadWritePaths=/opt/mybot
[Install]
WantedBy=multi-user.target
Для discord.js укажите ExecStart=/usr/bin/node /opt/mybot/index.js и уберите строку PYTHONUNBUFFERED. Важные директивы:
Restart=alwaysперезапускает процесс при любом завершении, и при сбое, и при штатномsys.exit();RestartSec=5не даёт боту, который падает мгновенно (неверный токен, синтаксическая ошибка), крутиться в плотном цикле.Wants=/After=network-online.targetоткладывают запуск до поднятия сети, чтобы первый вход после перезагрузки не падал на DNS.PYTHONUNBUFFERED=1: без терминала Python буферизуетstdoutблоками, и строкиprint()попадают в journal с опозданием на минуты или только при выходе.- Строки
Protect*,PrivateTmpиNoNewPrivilegesделают остальную файловую систему для бота доступной только на чтение или невидимой; каталоги, в которые ему нужно писать, добавьте вReadWritePaths=.
Шаг 2: включите, запустите и проверьте
sudo systemctl daemon-reload
sudo systemctl enable --now mybot
systemctl status mybot
sudo systemctl kill --signal=KILL mybot # simulate a crash; status is active again after 5 s
enable создаёт символическую ссылку для автозапуска, а --now запускает юнит. Строка kill служит настоящей проверкой: если через несколько секунд systemctl status mybot показывает active (running), политика перезапуска работает, а sudo reboot подтверждает запуск при загрузке. Чтобы увидеть его рядом с остальными службами, выведите список запущенных служб через systemctl; он отображается как mybot.service, а systemctl is-enabled mybot подтверждает, что он вернётся после перезагрузки.
Альтернатива: pm2 и сравнение со screen, nohup и Docker
Любой способ держать бота живым отвечает на три вопроса: переживает ли он выход из SSH, перезапускается ли при сбое и стартует ли при перезагрузке?
| Метод | Переживает выход из SSH | Перезапуск при сбое | Старт при перезагрузке | Вывод |
|---|---|---|---|---|
nohup / & | Yes | No | No | Только для быстрых тестов. |
screen / tmux | Yes | No | Нет, если не написать скрипт | Для отладки, не для продакшена. |
| pm2 | Yes | Yes | Да, после pm2 startup + pm2 save | Удобно командам на Node.js: один CLI для логов и перезапусков. |
| Юнит systemd | Yes | Да (Restart=) | Да (enable) | Рекомендуется: без стороннего софта, защита встроена. |
Docker, restart: unless-stopped | Yes | Yes | Да, вместе с демоном Docker | Хорошо, если бот уже поставляется образом. |
pm2 является популярным выбором в мире Node.js, он справляется и с Python. Запускайте его от учётной записи, которой принадлежит бот:
sudo npm install -g pm2
cd /opt/mybot
pm2 start index.js --name mybot
pm2 start bot.py --name mybot --interpreter /opt/mybot/.venv/bin/python # Python variant
pm2 save
pm2 startup # prints a sudo command to run once
pm2 logs mybot
pm2 install pm2-logrotate # ~/.pm2/logs/ otherwise grows forever
pm2 startup генерирует юнит systemd pm2-<user>, который при загрузке восстанавливает сохранённый список процессов, то есть pm2 работает поверх systemd, а не вместо него, и это честный довод в пользу прямого использования systemd для одного бота. pm2 оправдывает себя при нескольких процессах Node или в команде, которая уже с ним знакома.
Если вам ближе контейнеры, эквивалентом служит restart: unless-stopped в файле Compose; руководство по установке Docker на Ubuntu VPS описывает движок, Compose и поведение при загрузке. Оговорка про ufw бота не касается: он не публикует порты.
Логи, обновления и расчёт памяти
Логи
journalctl -u mybot -f # follow live
journalctl -u mybot --since "1 hour ago"
journalctl -u mybot -p err -b # errors since boot
sudo journalctl --vacuum-size=200M # cap journal size
journald сам выполняет ротацию и сжатие. Если бот пишет собственный лог-файл, используйте logging.handlers.RotatingFileHandler в Python или ротируемый транспорт pino либо winston в Node, чтобы болтливый обработчик не забил диск.
Обновления
cd /opt/mybot
sudo -u bot git pull
sudo -u bot .venv/bin/pip install -U -r requirements.txt # Node: sudo -u bot npm ci
sudo systemctl restart mybot
Перезапуск разрывает соединение с gateway на несколько секунд; библиотека переподключается, и пользователи обычно этого не замечают. Фиксируйте версии библиотек в requirements.txt или package-lock.json и читайте changelog перед мажорным обновлением. Держите систему актуальной с помощью sudo apt update && sudo apt upgrade или unattended-upgrades.
Сколько оперативной памяти нужно боту?
Не опирайтесь на общие цифры — измеряйте. systemctl status mybot выводит строку Memory:, то же показывает pm2 list. Бот на discord.py только с командами обычно простаивает в пределах десятков мегабайт; бот на discord.js несёт сверху среду выполнения Node. Память растёт вместе с состоянием: кэши участников и сообщений на крупных серверах, голос и музыка (по процессу ffmpeg на поток), генерация изображений или база данных. На практике 1 ГБ ОЗУ спокойно тянет систему плюс небольшого или среднего бота; боты с активным голосом или несколько ботов на одной машине оправдывают 2 ГБ и больше, а свыше 2500 серверов Discord требует шардинга, который умножает потребление. Две дешёвые страховки: MemoryMax=512M в юните, чтобы утечка перезапускала бота, а не роняла машину, и небольшой файл подкачки.
Всё это предполагает Linux-машину с root-доступом, работающую круглосуточно. Если такой машины ещё нет, VPS для Discord-бота от rdp.monster готов примерно через 10 секунд после подтверждения оплаты: выделенные CPU и ОЗУ, полный root-доступ, безлимитный трафик (fair-use), без KYC, приём криптовалюты, от $8.99 в месяц, так что шаг systemctl enable --now mybot может стать последним, что вы наберёте перед тем, как бот загорится зелёным.
Часто задаваемые вопросы
Можно ли хостить Discord-бота бесплатно 24/7?
systemd перезапускает бота при сбое и перезагрузке и никто не приостанавливает его за простой.Нужен ли Discord-боту статический IP или открытые порты на VPS?
Можно ли запускать несколько Discord-ботов на одном VPS?
/opt/bot-a и /opt/bot-b, свой .env со своим токеном и свой файл юнита, затем выполните systemctl enable --now для каждого. Отдельные системные пользователи не дадут ошибке в одном боте прочитать токен другого. Общий бюджет только один (ОЗУ и CPU): следите за systemctl status по каждому юниту и переходите на больший тариф, когда сумма приближается к пределу.На чём хостить Discord-бота: Windows или Linux VPS?
systemd в десять строк. На Windows для того же автозапуска после перезагрузки пришлось бы использовать Task Scheduler или NSSM и больше кликать.Adrien Roche, Редактор по инфраструктуре и хостингу
Системный инженер с более чем 10-летним опытом эксплуатации парков Windows Server и Linux. Адриен ведёт документацию по инфраструктуре rdp.monster и пишет наши руководства по RDP, VPS-хостингу, администрированию серверов, сетям и инструментам приватности.




