Как настроить автоматический рестарт сервера

1. Подготовка окружения и проверка доступа

Перед настройкой автозапуска убедитесь, что у вас есть полный доступ к выделенному серверу через SSH. В панели proxify.cloud (Proxmox VE) откройте консоль вашей виртуальной машины с Ubuntu или подключитесь по SSH с локальной машины.

ssh root@<IP-адрес_вашего_сервера>

Проверьте, где находится исполняемый файл сервера (обычно paper.jar, spigot.jar или fabric-server-launch.jar) и рабочая директория. Стандартный путь для игровых серверов на Ubuntu — /home/minecraft/server или /opt/minecraft. Дальше в инструкции используется путь /home/minecraft/server. Замените его на свой при необходимости.

ls -la /home/minecraft/server/
# Ожидаемый вывод: server.jar, start.sh, eula.txt, plugins/, world/

Убедитесь, что пользователь minecraft существует и имеет права на папку. Если сервер запускался от root — смените владельца:

chown -R minecraft:minecraft /home/minecraft/server

2. Создание скрипта управления запуском (start.sh)

Автоматический рестарт требует надежного скрипта запуска, который корректно обрабатывает сигналы остановки и перезапуска. Создайте или отредактируйте файл start.sh в директории сервера.

nano /home/minecraft/server/start.sh

Вставьте следующий контент. Параметры -Xms и -Xmx настройте под объем RAM вашего выделенного сервера на proxify.cloud (например, 8G, 16G, 32G). Флаг -XX:+UseG1GC рекомендуется для современных версий Minecraft (1.18+).

#!/bin/bash
# Путь к Java (проверьте: which java)
JAVA_PATH="/usr/bin/java"
# Память
HEAP_MIN="4G"
HEAP_MAX="8G"
# Файл сервера
SERVER_JAR="paper.jar"
# Имя сессии screen
SESSION_NAME="mcserver"
# Рабочая директория
WORK_DIR="/home/minecraft/server"

cd "$WORK_DIR" || exit 1

# JVM флаги оптимизации для Paper/Spigot
JVM_OPTS=(
  -Xms${HEAP_MIN} -Xmx${HEAP_MAX}
  -XX:+UseG1GC
  -XX:+ParallelRefProcEnabled
  -XX:MaxGCPauseMillis=200
  -XX:+UnlockExperimentalVMOptions
  -XX:+DisableExplicitGC
  -XX:+AlwaysPreTouch
  -XX:G1NewSizePercent=30
  -XX:G1MaxNewSizePercent=40
  -XX:G1HeapRegionSize=8M
  -XX:G1ReservePercent=20
  -XX:G1HeapWastePercent=5
  -XX:G1MixedGCCountTarget=4
  -XX:InitiatingHeapOccupancyPercent=15
  -XX:G1MixedGCLiveThresholdPercent=90
  -XX:G1RSetUpdatingPauseTimePercent=5
  -XX:SurvivorRatio=32
  -XX:+PerfDisableSharedMem
  -Dusing.aikars.flags=https://mcflags.emc.gs
  -Daikars.new.flags=true
)

# Запуск внутри screen для возможности подключения к консоли
exec ${JAVA_PATH} "${JVM_OPTS[@]}" -jar ${SERVER_JAR} --nogui

Сделайте скрипт исполняемым:

chmod +x /home/minecraft/server/start.sh
chown minecraft:minecraft /home/minecraft/server/start.sh

Важно: Тестовый запуск от пользователя minecraft:

su - minecraft -c "cd /home/minecraft/server && ./start.sh"

Убедитесь, что сервер стартует, мир загружается, и в консоли нет ошибок OutOfMemoryError или UnsupportedClassVersionError. Остановите сервер командой stop в консоли или Ctrl+C.

3. Настройка Systemd-юнита для автозапуска и рестарта

Systemd — стандартный менеджер служб в Ubuntu. Он обеспечит запуск при загрузке ОС, автоматический рестарт при краше и корректное завершение при перезагрузке хоста (через панель proxify.cloud).

Создайте файл юнита:

nano /etc/systemd/system/minecraft.service

Вставьте конфигурацию. Обратите внимание на User=minecraft, WorkingDirectory, ExecStart и политику Restart=on-failure.

[Unit]
Description=Minecraft Paper Server
Documentation=https://papermc.io/
After=network.target network-online.target
Wants=network-online.target
# Защита от запуска до монтирования дисков, если используется отдельный том
# RequiresMountsFor=/home/minecraft/server

[Service]
Type=simple
User=minecraft
Group=minecraft
WorkingDirectory=/home/minecraft/server
ExecStart=/home/minecraft/server/start.sh
# Перезапуск при краше (exit code != 0) или сигнале SIGSEGV/SIGABRT
Restart=on-failure
RestartSec=10
# Лимит на количество рестартов за интервал (защита от bootloop)
StartLimitIntervalSec=60
StartLimitBurst=3
# Корректная остановка: отправляем 'stop' в stdin процесса Java
ExecStop=/bin/bash -c 'echo "stop" | /usr/bin/screen -S mcserver -X stuff "$(printf \"stop\r\")"'
# Если stop не сработал за 30 сек — SIGKILL
TimeoutStopSec=30
KillSignal=SIGTERM
SendSIGKILL=yes
# Логирование в journald
StandardOutput=journal
StandardError=journal
SyslogIdentifier=minecraft
# Ограничение ресурсов (опционально, для безопасности)
LimitNOFILE=65535
MemoryLimit=10G
CPUAccounting=yes

[Install]
WantedBy=multi-user.target

Пояснение по ExecStop: В данном примере предполагается, что вы используете screen внутри start.sh (см. шаг 2). Если вы запускаете Java напрямую без screen, замените ExecStop на:

ExecStop=/bin/bash -c 'echo "stop" > /home/minecraft/server/console.pipe'

И настройте named pipe во start.sh. Вариант с screen проще и надежнее для консольного доступа.

Перезагрузите конфигурацию systemd и включите автозапуск:

systemctl daemon-reload
systemctl enable minecraft.service

Запустите сервер через systemd и проверьте статус:

systemctl start minecraft.service
systemctl status minecraft.service
journalctl -u minecraft.service -f

4. Настройка планировщика (Cron) для ежедневного рестарта

Ежедневный рестарт (например, в 04:00 ночи) очищает утечки памяти, сбрасывает кэш чанков и предупреждает лаги. Лучше всего делать это через systemd-timer или cron. Используем cron для простоты и наглядности.

Откройте crontab от имени пользователя minecraft (не root, чтобы права на файлы мира не сломались):

crontab -u minecraft -e

Добавьте в конец файла:

# Ежедневный рестарт в 04:00 с предупреждением за 5, 1, 0.5 минуты
0 4 * * * /home/minecraft/server/restart_wrapper.sh >> /home/minecraft/server/logs/cron_restart.log 2>&1

Теперь создайте скрипт-обертку restart_wrapper.sh, который корректно предупредит игроков и перезапустит службу systemd. Прямой вызов systemctl restart из cron от пользователя minecraft не сработает без sudo (policykit). Поэтому используем подход с screen командой stop, а systemd поднимет процесс обратно благодаря Restart=on-failure (если мы убьем процесс) или лучше — используем systemctl через sudoers без пароля.

Вариант А: Через sudo (рекомендуется, чище)

Настройте sudoers для пользователя minecraft:

visudo -f /etc/sudoers.d/minecraft-restart

Добавьте строку:

minecraft ALL=(ALL) NOPASSWD: /bin/systemctl restart minecraft.service, /bin/systemctl stop minecraft.service, /bin/systemctl start minecraft.service

Теперь скрипт обертки /home/minecraft/server/restart_wrapper.sh:

nano /home/minecraft/server/restart_wrapper.sh
#!/bin/bash
# Скрипт мягкого рестарта с предупреждениями игроков
SESSION="mcserver"
SERVER_DIR="/home/minecraft/server"
LOG_FILE="${SERVER_DIR}/logs/restart_$(date +\%F).log"

# Функция отправки команды в консоль screen
send_cmd() {
    /usr/bin/screen -S "$SESSION" -X stuff "$(printf "%s\r" "$1")"
}

echo "[$(date)] === Начало процедуры рестарта ===" | tee -a "$LOG_FILE"

# Предупреждение за 5 минут
send_cmd "say §e[Авторестарт] §fСервер перезагрузится через §c5 минут§f. Пожалуйста, завершите дела."
sleep 4m

# Предупреждение за 1 минуту
send_cmd "say §e[Авторестарт] §fСервер перезагрузится через §c1 минуту§f."
sleep 50s

# Предупреждение за 10 секунд
send_cmd "say §e[Авторестарт] §fСервер перезагрузится через §c10 секунд§f."
sleep 10s

# Сохранение мира
send_cmd "save-all"
send_cmd "save-off"
sleep 5

# Остановка через systemd (корректный SIGTERM -> stop -> SIGKILL)
echo "[$(date)] Отправка команды systemctl restart..." | tee -a "$LOG_FILE"
sudo /bin/systemctl restart minecraft.service

echo "[$(date)] Команда рестарта отправлена. Systemd поднимет сервер." | tee -a "$LOG_FILE"

Дайте права:

chmod +x /home/minecraft/server/restart_wrapper.sh
chown minecraft:minecraft /home/minecraft/server/restart_wrapper.sh

Вариант Б: Без sudo (через kill процесса Java)

Если не хотите настраивать sudoers, можно убить процесс Java, и systemd поднимет его заново (так как Restart=on-failure срабатывает и на SIGKILL/SIGTERM, если код возврата не 0). Но это "грубый" способ, мир может не успеть сохраниться (save-all отправляется, но нет гарантии завершения).

В restart_wrapper.sh замените строку systemctl restart на:

# Находим PID java процесса в конкретной screen сессии или по рабочей директории
PID=$(pgrep -f "java.*${SERVER_JAR}.*${WORK_DIR}")
if [ -n "$PID" ]; then
    kill -TERM "$PID"
    # Ждем 20 сек
    sleep 20
    # Если жив - убиваем принудительно
    if kill -0 "$PID" 2>/dev/null; then