RDP Monster

Хостинг Discord-бота на VPS: держим бота онлайн 24/7

Хостинг Discord-бота на VPS: держим бота онлайн 24/7

Почему именно на 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 / &YesNoNoТолько для быстрых тестов.
screen / tmuxYesNoНет, если не написать скриптДля отладки, не для продакшена.
pm2YesYesДа, после pm2 startup + pm2 saveУдобно командам на Node.js: один CLI для логов и перезапусков.
Юнит systemdYesДа (Restart=)Да (enable)Рекомендуется: без стороннего софта, защита встроена.
Docker, restart: unless-stoppedYesYesДа, вместе с демоном 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?

Надёжно уже нет: бесплатные тарифы, которые держали ботов активными, закрыты или засыпают при простое.
Heroku отказался от бесплатных dyno в ноябре 2022 года, Replit перенёс always-on в платные развёртывания, а Glitch закрыл хостинг приложений в 2025 году. Оставшиеся бесплатные веб-тарифы засыпают при простое, что закрывает соединение бота с gateway. Запуск на своём ПК работает ровно до перезагрузки или ухода компьютера в сон. Самым дешёвым надёжным вариантом будет небольшой VPS примерно с 1 ГБ ОЗУ, где systemd перезапускает бота при сбое и перезагрузке и никто не приостанавливает его за простой.

Нужен ли Discord-боту статический IP или открытые порты на VPS?

Нет: бот подключается к Discord исходящим соединением, поэтому входящие порты открывать не нужно, а IP значения не имеет.
Бот на gateway открывает исходящее WebSocket-соединение с Discord и получает события по нему же, поэтому файрвол на VPS может быть закрыт для всего, кроме SSH, а IP сервера пользователям вообще не виден. Открытый порт и публичное имя хоста нужны только при переходе на HTTP-эндпойнты взаимодействий, которые Discord вызывает по зарегистрированному вами URL, либо при добавлении веб-панели. В этом случае разместите её за HTTPS с обратным прокси.

Можно ли запускать несколько Discord-ботов на одном VPS?

Да: по одному юниту systemd (или процессу pm2) на бота, в идеале каждый под своим пользователем и в своём каталоге.
Ничто не ограничивает вас одним ботом на машину. Дайте каждому боту свой каталог, например /opt/bot-a и /opt/bot-b, свой .env со своим токеном и свой файл юнита, затем выполните systemctl enable --now для каждого. Отдельные системные пользователи не дадут ошибке в одном боте прочитать токен другого. Общий бюджет только один (ОЗУ и CPU): следите за systemctl status по каждому юниту и переходите на больший тариф, когда сумма приближается к пределу.

На чём хостить Discord-бота: Windows или Linux VPS?

Linux служит вариантом по умолчанию для ботов: дешевле, легче, а инструменты (systemd, pm2, Docker) сделаны под него.
И discord.py, и discord.js работают на Windows, и Windows VPS будет верным выбором, если бот зависит от приложения только под Windows или вам просто удобнее рабочий стол. Для обычного бота выигрывает Linux: система расходует малую долю ОЗУ, минимальный сервер почти не имеет поверхности атаки, а для поддержания процесса живым хватает юнита systemd в десять строк. На Windows для того же автозапуска после перезагрузки пришлось бы использовать Task Scheduler или NSSM и больше кликать.

Adrien Roche, Редактор по инфраструктуре и хостингу

Системный инженер с более чем 10-летним опытом эксплуатации парков Windows Server и Linux. Адриен ведёт документацию по инфраструктуре rdp.monster и пишет наши руководства по RDP, VPS-хостингу, администрированию серверов, сетям и инструментам приватности.

Регистрация в программе для реселлеров

Ваши данные

Если у вас есть вопросы, напишите нам, нажав сюда !
Имя и фамилия(Обязательно)
Укажите ваш email: у вас должен быть аккаунт на manager.rdp.monster !

Ваша компания

Укажите адрес вашего сайта, если он у вас есть
Кратко опишите, как планируете продавать наши услуги клиентам. Например, через общение на форумах.

Мы используем файлы cookie!

Мы используем файлы cookie, чтобы сделать сайт удобнее, показывать персонализированную рекламу и контент и анализировать трафик. Нажимая «Принять», вы соглашаетесь на использование файлов cookie.