Боты для автоматизации бэкапов игрового сервера
Бэкап, который тихо падает по ночам и никому об этом не сообщает, — почти то же самое, что бэкапа нет вообще. Узнаёте об этом обычно в худший момент: сервер лёг, лезете в хранилище восстанавливать мир, а там последний рабочий архив недельной давности, потому что диск на хостинге переполнился ещё во вторник. Ниже — рабочая схема, которая закрывает именно этот разрыв: cron гонит архивацию и заливку во внешнее хранилище, а бот в Discord или Telegram присылает вам короткий отчёт «бэкап ок, 412 МБ» или «бэкап упал, смотри лог» сразу после каждого прогона.
Содержание
Зачем тут вообще бот, а не просто cron
Базовую схему «архивируй по расписанию» мы уже разбирали в статье про настройку автобэкапов через cron — она рабочая, но у неё есть слепое пятно: если скрипт упал в 4 утра, узнаете вы об этом только когда полезете за архивом, то есть когда он уже нужен позарез. Почта от cron (MAILTO) в 2026 году читается раз в неделю, если вообще настроена, а вот в игровой Discord или личку в Telegram админы заглядывают в течение дня почти гарантированно.
Смысл бота здесь простой и без излишней магии: он не заменяет cron-скрипт, а оборачивает его отчётностью и, по желанию, ручным управлением. Три вещи, которые он даёт сверху голого cron:
- Уведомление в чат после каждого прогона — успех с размером архива и временем, либо ошибка с последними строками лога.
- Команду вручную —
/backup nowв Discord или/backupв Telegram, чтобы снять внеплановую копию перед рискованным апдейтом мода, не заходя по SSH. - Статус на запрос —
/backup status, который покажет дату последнего успешного бэкапа и его размер, не копаясь в консоли.
Если бота на сервере ещё нет вообще, начните с базовой установки — Discord-бот на VPS или Telegram-бот на VPS — там про токен, автозапуск через systemd и безопасное хранение секретов. Здесь считаем, что процесс бота уже крутится 24/7, и добавляем к нему логику бэкапов.
Архитектура: кто за что отвечает
Не пытайтесь засунуть архивацию, загрузку в облако и логику Discord-бота в один монолитный скрипт — вы наплачетесь с отладкой при первой же ошибке. Разделяйте на три независимых куска:
backup.sh— bash-скрипт, который архивирует папку сохранений и кладёт tar.gz в локальную директорию/opt/backups/. Ничего не знает ни про облако, ни про чаты.upload.sh(или Python-скрипт) — берёт свежий архив из/opt/backups/и льёт его в S3-совместимое хранилище или Google Drive. Тоже ничего не знает про Discord/Telegram.notify.py/ часть бота — читает код возврата (exit code) первых двух шагов и лог, формирует сообщение и отправляет его через Discord Webhook или Telegram Bot API.
Склеивает всё это либо один общий shell-скрипт-обёртка, который вызывает три части по очереди и передаёт $? дальше, либо сам бот, если вы хотите команду /backup now — тогда бот запускает обёртку через subprocess/child_process и по завершении шлёт результат в тот же канал, откуда пришла команда.
#!/usr/bin/env bash
# /opt/scripts/backup_pipeline.sh — обёртка над тремя шагами
set -uo pipefail
LOG=/var/log/gameserver-backup.log
/opt/scripts/backup.sh >>"$LOG" 2>&1
ARCHIVE_STATUS=$?
if [ $ARCHIVE_STATUS -eq 0 ]; then
/opt/scripts/upload.sh >>"$LOG" 2>&1
UPLOAD_STATUS=$?
else
UPLOAD_STATUS=1
fi
python3 /opt/scripts/notify.py --archive $ARCHIVE_STATUS --upload $UPLOAD_STATUS --log "$LOG"
Обратите внимание на set -uo pipefail без -e — так скрипт не оборвётся молча, а дойдёт до notify.py даже при ошибке на любом шаге, и вы получите отчёт о падении, а не тишину.
Нужен игровой сервер?
MAATRIX GAMES — 45+ игр, деплой за минуты, DDoS-защита, автобэкапы. Тарифы от 399 ₽/мес.
Создать серверАрхивация: что и как паковать
Логику архивации подробно разбирали в статье про автобэкапы, здесь — минимальный рабочий вариант для примера, годится для Minecraft, легко адаптируется под Rust/ARK заменой пути:
#!/usr/bin/env bash
# /opt/scripts/backup.sh
set -euo pipefail
SRC="/opt/minecraft/world"
DEST="/opt/backups"
STAMP=$(date +%Y-%m-%d_%H-%M)
FILE="$DEST/mc-world-$STAMP.tar.gz"
mkdir -p "$DEST"
tar -czf "$FILE" -C /opt/minecraft world server.properties
# ротация: держим последние 14 архивов, остальное чистим
ls -1t "$DEST"/mc-world-*.tar.gz | tail -n +15 | xargs -r rm --
echo "OK: $FILE ($(du -h "$FILE" | cut -f1))"
Ключевой момент — тар выполняется штатной командой tar, без остановки сервера. Если игра чувствительна к записи «на живую» (это касается баз данных плагинов, а не самих файлов мира), делайте save-off/save-all через RCON перед архивацией, как описано в статье про восстановление сервера из бэкапа — там же про то, почему нельзя лить архив поверх работающего процесса.
Загрузка в S3-совместимое хранилище
Для S3-совместимых бакетов (AWS S3, Backblaze B2, Wasabi, MinIO на своём железе) проще всего взять rclone — один бинарник, не требует Python-зависимостей и одинаково работает с любым S3-подобным провайдером.
Настройка один раз:
rclone config
# n) New remote → name: b2backup → тип: s3 (или b2 для Backblaze нативно)
# endpoint, access_key_id, secret_access_key — из панели провайдера
Сам скрипт заливки:
#!/usr/bin/env bash
# /opt/scripts/upload.sh
set -euo pipefail
LATEST=$(ls -t /opt/backups/mc-world-*.tar.gz | head -1)
rclone copy "$LATEST" b2backup:my-gameserver-backups/ --progress
echo "Uploaded: $(basename "$LATEST")"
rclone умеет и шифрование на лету (rclone crypt), если архив с данными игроков не должен лежать в открытом виде на стороннем сервере — стоит включить, особенно для RP-серверов с личными данными в БД экономики.
Загрузка в Google Drive через API
Google Drive удобен тем, что бесплатная квота обычно достаточна для игрового сервера, но требует чуть больше телодвижений — сервисный аккаунт и OAuth вместо ключа-пароля.
- В Google Cloud Console создайте проект, включите Google Drive API.
- Создайте сервисный аккаунт (Service Account), скачайте JSON-ключ.
- Расшарьте целевую папку на Drive на email сервисного аккаунта (он выглядит как
backup-bot@project-id.iam.gserviceaccount.com) с правом «Редактор» — иначе бот увидит папку, но не сможет писать в неё.
Минимальный загрузчик на Python (google-api-python-client):
# /opt/scripts/upload_gdrive.py
from google.oauth2 import service_account
from googleapiclient.discovery import build
from googleapiclient.http import MediaFileUpload
import glob, os
SCOPES = ["https://www.googleapis.com/auth/drive.file"]
KEY = "/opt/scripts/gdrive-key.json"
FOLDER_ID = "1AbCdEfGhIjKlMnOpQrStUvWxYz" # id папки на Drive
creds = service_account.Credentials.from_service_account_file(KEY, scopes=SCOPES)
service = build("drive", "v3", credentials=creds)
latest = max(glob.glob("/opt/backups/mc-world-*.tar.gz"), key=os.path.getmtime)
meta = {"name": os.path.basename(latest), "parents": [FOLDER_ID]}
media = MediaFileUpload(latest, mimetype="application/gzip", resumable=True)
service.files().create(body=meta, media_body=media, fields="id").execute()
print(f"Uploaded: {latest}")
resumable=True не роскошь — для архива в несколько гигабайт (актуально для ARK или Rust с большой картой) это спасает от необходимости заливать всё заново при обрыве соединения.
| Вариант | Плюсы | Минусы |
|---|---|---|
| S3-совместимое (rclone) | Быстро, дёшево при больших объёмах, шифрование из коробки | Нужен внешний провайдер и его оплата |
| Google Drive API | Часто хватает бесплатной квоты, знакомый интерфейс для просмотра архивов | Сложнее настройка (OAuth/сервисный аккаунт), лимиты API на частые загрузки |
Уведомления в Discord и Telegram
Для Discord проще всего обойтись без полноценного бота — Webhook канала: создаётся в настройках канала (Integrations → Webhooks → New Webhook), даёт URL, на который можно слать POST без токена и логина бота.
# кусок notify.sh для Discord
WEBHOOK="https://discord.com/api/webhooks/XXXX/YYYY"
if [ "$STATUS" = "ok" ]; then
MSG="✅ Бэкап Minecraft: $SIZE, $(date '+%d.%m %H:%M')"
else
MSG="❌ Бэкап упал на шаге $STEP, см. лог на сервере"
fi
curl -s -H "Content-Type: application/json" \
-d "{\"content\": \"$MSG\"}" "$WEBHOOK" >/dev/null
Для Telegram нужен токен бота и chat_id (получить его проще всего через @userinfobot или из апдейта после первого сообщения боту):
TOKEN="123456789:AAHdqTcvCH1vGWJxfSeofSAs0K5PALDsaw"
CHAT_ID="-1001234567890"
curl -s -X POST "https://api.telegram.org/bot$TOKEN/sendMessage" \
-d chat_id="$CHAT_ID" -d text="$MSG" >/dev/null
Если хочется не просто уведомление, а именно интерактивного бота с командой /backup now, добавьте в уже работающего discord.js-бота обработчик, который запускает backup_pipeline.sh и стримит результат обратно в тот же канал:
const { exec } = require('child_process');
client.on('interactionCreate', async (i) => {
if (i.commandName !== 'backup') return;
await i.reply('Запускаю бэкап…');
exec('/opt/scripts/backup_pipeline.sh', (err, stdout) => {
i.followUp(err ? `❌ Ошибка: ${err.message}` : `✅ Готово:\n${stdout}`);
});
});
Права на выполнение скрипта у пользователя, от которого работает бот, должны быть строго ограничены sudo-правилами именно на этот скрипт (visudo, NOPASSWD только для конкретного пути) — не давайте процессу бота полный root, это отдельная и частая дыра в безопасности таких интеграций.
Планировщик и мониторинг самого пайплайна
Собираем всё через cron или systemd timer — второй вариант удобнее, если хотите видеть статус юнита и логи через journalctl:
# /etc/systemd/system/gs-backup.service
[Unit]
Description=Game server backup pipeline
[Service]
Type=oneshot
ExecStart=/opt/scripts/backup_pipeline.sh
# /etc/systemd/system/gs-backup.timer
[Unit]
Description=Run gs-backup daily at 04:00
[Timer]
OnCalendar=*-*-* 04:00:00
Persistent=true
[Install]
WantedBy=timers.target
Persistent=true — важная деталь: если сервер был выключен или перезагружался в момент срабатывания таймера, systemd досрочно прогонит пропущенный бэкап при следующем старте, а не будет молча ждать следующего дня. Активируется командой systemctl enable --now gs-backup.timer.
Отдельно стоит присматривать за самим пайплайном: если бот не прислал ни одного сообщения за 26+ часов (сутки плюс запас) — это тоже сигнал проблемы, даже без явной ошибки. Простая проверка: скрипт пишет метку времени в файл при каждом успешном запуске, а отдельный cron-джоб раз в час сверяет mtime этого файла и шлёт тревогу, если бэкап не подтверждался слишком долго. Так ловится не только падение скрипта, но и, например, остановка самого cron-демона или падение процесса бота.
Нужен игровой сервер?
MAATRIX GAMES — 45+ игр, деплой за минуты, DDoS-защита, автобэкапы. Тарифы от 399 ₽/мес.
Создать серверЧастые вопросы
Можно ли использовать только один канал уведомлений — либо Discord, либо Telegram?
Да, вторую часть notify-скрипта просто не подключайте. Многие держат оба: Discord для команды разработки/админов, Telegram — как личный алерт на телефон, потому что push от Telegram обычно надёжнее долетает на мобильный.
Что если архив получился нулевого размера — это тоже считается ошибкой?
Обязательно проверяйте это отдельно: tar иногда завершается с кодом 0, даже если исходная папка была пустой из-за опечатки в пути. Добавьте в backup.sh проверку [ -s "$FILE" ] перед тем как считать шаг успешным.
Нужно ли шифровать архив перед заливкой в облако?
Если в бэкапе только файлы мира — не критично. Если там базы с email/IP игроков (экономика, тикет-система, RP-сервера) — да, rclone crypt или gpg --symmetric перед загрузкой обязательны, особенно для GDPR-чувствительных проектов.
Как часто гонять полный бэкап, если сервер большой (Rust/ARK на десятки гигабайт)?
Раз в сутки полный плюс инкрементальные снапшоты чаще (например, через rsync --link-dest с хардлинками) — это экономит и место в облаке, и трафик на заливку. Полный бэкап раз в неделю, инкременты ежедневно — рабочий компромисс для большинства survival-проектов.
Бот может случайно запустить два бэкапа одновременно, если дёрнуть команду вручную во время cron-прогона?
Может, и это создаёт гонку за диск и сеть. Добавьте лок-файл в начало backup_pipeline.sh (flock -n /tmp/gs-backup.lock ...) — второй запуск просто откажется стартовать, пока первый не завершится, и бот пришлёт об этом отдельное сообщение вместо тихого зависания.