MAATRIX GAMES / Блог / Боты для автоматизации бэкапов игрового сервера

Боты для автоматизации бэкапов игрового сервера

MAATRIX GAMES

Бэкап, который тихо падает по ночам и никому об этом не сообщает, — почти то же самое, что бэкапа нет вообще. Узнаёте об этом обычно в худший момент: сервер лёг, лезете в хранилище восстанавливать мир, а там последний рабочий архив недельной давности, потому что диск на хостинге переполнился ещё во вторник. Ниже — рабочая схема, которая закрывает именно этот разрыв: 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-бота в один монолитный скрипт — вы наплачетесь с отладкой при первой же ошибке. Разделяйте на три независимых куска:

  1. backup.sh — bash-скрипт, который архивирует папку сохранений и кладёт tar.gz в локальную директорию /opt/backups/. Ничего не знает ни про облако, ни про чаты.
  2. upload.sh (или Python-скрипт) — берёт свежий архив из /opt/backups/ и льёт его в S3-совместимое хранилище или Google Drive. Тоже ничего не знает про Discord/Telegram.
  3. 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 вместо ключа-пароля.

  1. В Google Cloud Console создайте проект, включите Google Drive API.
  2. Создайте сервисный аккаунт (Service Account), скачайте JSON-ключ.
  3. Расшарьте целевую папку на 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 ...) — второй запуск просто откажется стартовать, пока первый не завершится, и бот пришлёт об этом отдельное сообщение вместо тихого зависания.