MAATRIX GAMES / Блог / Автоматический рестарт сервера по расписанию (cron)

Автоматический рестарт сервера по расписанию (cron)

MAATRIX GAMES

Если сервер живёт неделями без перезапуска, рано или поздно начинается знакомая картина: тикрейт проседает, память жрётся быстрее обычного, а иногда сервер просто зависает без единой строчки в логах о причине. Плановый рестарт по расписанию — самый скучный и при этом самый надёжный способ этого избежать. Ниже — рабочая схема на cron и systemd, которую можно поставить один раз и забыть.

Зачем вообще нужен плановый рестарт

Игровые серверы — это долгоживущие процессы, и почти любой из них со временем накапливает деградацию: утечки памяти в Java-мирах Minecraft, разрастание списка сущностей в Rust, фрагментацию памяти в модифицированных сборках на Lua (FiveM, Garry's Mod). Даже если утечка небольшая — пара мегабайт в час — за неделю аптайма она превращается в заметное падение TPS или FPS на клиенте.

Есть и вторая причина, менее очевидная: плановый рестарт — это точка, в которую удобно "подвесить" остальную рутину. Ротацию логов, бэкап мира перед остановкой, применение обновлений, которые требуют перезапуска процесса (не хотфиксов на лету, а именно смены бинарника или конфига). Если рестарт всё равно происходит каждую ночь, логично туда же встроить и эти задачи, а не городить отдельные cron-джобы, которые могут пересечься с работающим сервером.

Отдельно стоит сказать честно: плановый рестарт — это не замена нормального мониторинга и не лечит первопричину утечки, если она есть в конкретной версии мода или плагина. Это симптоматическое решение, но оно рабочее и почти нулевой стоимости в настройке.

Базовая cron-задача

Cron — планировщик заданий, который есть в любом Linux-дистрибутиве. Открываем crontab пользователя, от которого работает сервер (не root, если можно этого избежать):

crontab -e

Простейший вариант — рестарт через systemd-юнит раз в сутки в 5:00 по времени сервера:

0 5 * * * systemctl restart minecraft-server.service

Формат полей — минута, час, день месяца, месяц, день недели. Если нужен рестарт не каждый день, а, скажем, только по средам и субботам:

0 5 * * 3,6 systemctl restart rust-server.service

Но такой прямолинейный вызов restart — это, по сути, kill с задержкой: systemd пошлёт сигнал остановки и через таймаут прибьёт процесс, если тот не завершится сам. Для мира, который не был корректно сохранён, это может означать откат прогресса на несколько минут или, в худшем случае, повреждение файла сохранения. Дальше разберём, как сделать это правильно.

Нужен игровой сервер?

MAATRIX GAMES — 45+ игр, деплой за минуты, DDoS-защита, автобэкапы. Тарифы от 399 ₽/мес.

Создать сервер

Предупреждаем игроков заранее

Резкий обрыв сессии — одна из главных причин негатива от игроков, даже если рестарт длится 30 секунд. Решение — рассылка предупреждений через RCON за 10, 5 и 1 минуту до остановки. RCON (Remote Console) есть практически во всех популярных серверах: Minecraft, Rust, CS2, ARK, Valheim (через плагины), Project Zomboid.

Пример для Minecraft через mcrcon (нужно установить: apt install mcrcon или собрать из исходников):

mcrcon -H 127.0.0.1 -P 25575 -p "ваш_rcon_pass" "say Внимание! Рестарт сервера через 10 минут."

Для Rust через rust-rcon или встроенный веб-сокет RCON (порт задаётся параметром +rcon.port):

rustcli --host 127.0.0.1 --port 28016 --password "ваш_rcon_pass" "say Перезапуск сервера через 10 минут, сохраните прогресс"

Для CS2 (использует стандартный Source RCON):

rcon -a 127.0.0.1:27015 -p "ваш_rcon_pass" "say [СЕРВЕР] Рестарт через 10 минут"

Соберём это в один скрипт-обёртку, который шлёт три предупреждения с паузами, а затем инициирует корректную остановку:

#!/bin/bash
# /opt/scripts/scheduled-restart.sh

RCON_HOST="127.0.0.1"
RCON_PORT="25575"
RCON_PASS="ваш_rcon_pass"
SERVICE="minecraft-server.service"

send_warn() {
  mcrcon -H "$RCON_HOST" -P "$RCON_PORT" -p "$RCON_PASS" "say $1"
}

send_warn "Плановый рестарт сервера через 10 минут"
sleep 300
send_warn "Плановый рестарт сервера через 5 минут"
sleep 240
send_warn "Плановый рестарт через 1 минуту, сохраняем мир"
sleep 60

mcrcon -H "$RCON_HOST" -P "$RCON_PORT" -p "$RCON_PASS" "save-all"
sleep 5
systemctl stop "$SERVICE"
sleep 3
systemctl start "$SERVICE"

Даём права на выполнение и меняем cron-задачу так, чтобы она вызывала скрипт, а не напрямую systemctl:

chmod +x /opt/scripts/scheduled-restart.sh
0 5 * * * /opt/scripts/scheduled-restart.sh >> /var/log/scheduled-restart.log 2>&1

Обратите внимание на >> /var/log/scheduled-restart.log 2>&1 — без этого вывод скрипта уйдёт в почту root (если настроен MTA) или потеряется вовсе, и вы не увидите, если что-то пошло не так.

Graceful shutdown вместо kill

Ключевая ошибка новичков — гасить процесс через kill -9 или pkill. Это моментально убивает процесс без сохранения состояния: для Minecraft это риск потерять последние минуты изменений в мире, для Rust — повредить файл сохранения, если запись шла в момент убийства процесса.

Правильная последовательность:

  1. Дать команду сохранения (для Minecraft — save-all, для Rust — server.save, для большинства ARK/Valheim серверов через RCON — соответствующая команда сохранения из документации конкретной игры).
  2. Дождаться завершения записи на диск (пара секунд для небольших миров, до 10-20 секунд для тяжёлых карт с сотнями структур).
  3. Отправить сигнал SIGTERM (это делает systemctl stop по умолчанию) — большинство серверных бинарников перехватывают его и корректно закрывают сокеты, дописывают логи, освобождают ресурсы.
  4. Только если процесс не завершился за разумный таймаут — SIGKILL как крайняя мера.

Systemd уже умеет делать шаги 3-4 сам через параметр TimeoutStopSec в юните — не нужно писать это вручную, достаточно правильно настроить сам unit-файл (см. следующий раздел). Ваша задача — гарантировать, что шаги 1-2 (сохранение) выполнены до того, как systemd пришлёт SIGTERM.

Для Rust, кстати, есть нюанс: команда server.save асинхронная, и сервер не сообщает через RCON явно "сохранение завершено". На практике достаточно паузы в 10-15 секунд после команды сохранения перед остановкой — для большинства карт этого хватает, но если у вас особо крупная кастомная карта, лучше проверить логи сервера и увеличить паузу.

Автозапуск обратно через systemd

Чтобы сервер не просто остановился, а гарантированно поднялся снова, нужен systemd-юнит с Restart=always. Пример для типового Java-сервера Minecraft (Paper):

# /etc/systemd/system/minecraft-server.service
[Unit]
Description=Minecraft Paper Server
After=network.target

[Service]
User=mcserver
WorkingDirectory=/home/mcserver/server
ExecStart=/usr/bin/java -Xms4G -Xmx6G -jar paper.jar nogui
Restart=on-failure
RestartSec=10
TimeoutStopSec=60
StandardInput=null

[Install]
WantedBy=multi-user.target

Обратите внимание на Restart=on-failure, а не Restart=always — так systemd не будет пытаться поднять сервер бесконечно по кругу, если тот падает сразу после старта из-за битого конфига (это защита от "restart loop", который может забить диск логами за считаные минуты). TimeoutStopSec=60 даёт процессу минуту на корректное закрытие после SIGTERM, прежде чем systemd пришлёт SIGKILL принудительно.

После создания или изменения юнита обязательно:

systemctl daemon-reload
systemctl enable minecraft-server.service

Если сервер запускается не напрямую бинарником, а через скрипт-обёртку (типичная ситуация для Rust с oxide/uMod, где нужно применить патчи после старта, или для FiveM с txAdmin), в ExecStart указывается путь к этому скрипту, а не к самому исполняемому файлу. Про сам процесс поднятия конкретных серверов подробно расписано в статьях по Rust и Minecraft.

Выбор времени рестарта

Тут нет универсального ответа — всё зависит от того, где живёт ваша основная аудитория. Несколько практических ориентиров:

  • RU-аудитория, сервер в UK или US-локации: смотрите на московское время, а не на время сервера. 5:00-6:00 по Москве обычно приходится на глубокую ночь по UTC, но это не совпадает с ночью по времени сервера — важно именно локальное время игроков, а не дата-центра.
  • Международный проект (EU+US): компромисс редко бывает идеальным. Часто выбирают время, минимальное для обоих поясов сразу — например, раннее утро по UTC, которое попадает на ночь в Европе и на поздний вечер/раннюю ночь на восточном побережье США.
  • Смотрите на реальные логи, а не угадывайте: если у вас включено логирование входов/выходов игроков, за неделю-две легко увидеть провал активности и поставить рестарт именно туда, а не полагаться на "все спят ночью".

Таблица для ориентира (часовые пояса сильно упрощены, проверяйте по факту):

АудиторияЛокация сервераОриентировочное окно рестарта (по времени аудитории)
RU/СНГUK/US04:00-06:00 (МСК)
EUUK03:00-05:00 (CET)
US EastUS03:00-05:00 (ET)
Смешанная RU+EUUK03:00-04:00 (МСК) — компромисс

Если у сервера несколько пиков активности (например, будни отличаются от выходных), не бойтесь ставить разное время или частоту через отдельные cron-строки — например, реже рестартить по выходным, когда трафик выше и обрыв сессии заметнее.

Логирование и проверка, что рестарт реально сработал

Слепая вера в то, что cron отработал — плохая практика. Минимальный набор проверок:

# последний запуск cron-задачи и её вывод
tail -50 /var/log/scheduled-restart.log

# статус юнита и время последнего старта
systemctl status minecraft-server.service

# убедиться, что cron-демон вообще жив
systemctl status cron

Полезно также добавить в конец скрипта проверку, что процесс реально поднялся и слушает порт, с алертом (например, отправкой сообщения в Telegram-бота или webhook), если этого не произошло:

sleep 30
if ! systemctl is-active --quiet "$SERVICE"; then
  curl -s -X POST "https://api.telegram.org/bot<TOKEN>/sendMessage" \
    -d chat_id=<CHAT_ID> \
    -d text="Внимание: сервер $SERVICE не поднялся после планового рестарта!"
fi

Это простой, но рабочий контур: не полагаться на "тишина = всё хорошо", а явно проверять факт успешного старта.

Нужен игровой сервер?

MAATRIX GAMES — 45+ игр, деплой за минуты, DDoS-защита, автобэкапы. Тарифы от 399 ₽/мес.

Создать сервер

Частые вопросы

Можно ли обойтись без RCON-предупреждений, если у меня маленький сервер на 3-5 друзей?

Можно, но всё равно стоит хотя бы кинуть сообщение в Discord-канал через webhook перед рестартом — это дешевле по времени настройки, чем RCON-скрипт, и почти так же полезно для небольшой команды.

Что если сервер не поддерживает RCON вообще?

Тогда предупреждение можно слать только через внешние каналы (Discord/Telegram), а для остановки использовать штатный механизм graceful shutdown самого сервера — почти у всех есть либо консольная команда сохранения через stdin, либо API. Проверяйте документацию конкретной игры.

Стоит ли рестартить сервер каждую ночь, если проблем с производительностью не замечено?

Не обязательно каждую ночь — можно начать с раза в 2-3 дня и смотреть на метрики (TPS, память, время отклика). Если деградации нет — оставьте расписание пореже, лишний рестарт — это тоже минута простоя.

Как быть с бэкапом мира — делать его до рестарта или после?

Логичнее до остановки, сразу после команды сохранения (save-all и аналоги) — так вы бэкапите гарантированно консистентное состояние, а не файлы, которые могут быть в процессе дозаписи после холодного старта.

Рестарт ломает моды/плагины, которые кэшируют состояние в памяти?

Изредка да — некоторые плагины экономики или квестов держат несохранённое состояние между "тиками" автосохранения. Проверьте документацию конкретного плагина на предмет хука вида onServerStop или аналогичного, и по возможности добавьте его вызов явно перед остановкой.