MAATRIX GAMES / Блог / DayZ: краш сервера при рестарте

DayZ: краш сервера при рестарте

MAATRIX GAMES

Сервер работал всю ночь без нареканий, а после планового рестарта в 5 утра просто не поднялся — в логах либо тишина, либо строчка про повреждённые данные. Знакомая картина для DayZ-админов: краш на рестарте почти никогда не связан с самим геймплеем, а почти всегда — с тем, что происходит в момент остановки и старта процесса. Разберём три самые частые причины и как их искать по логам, а не гадать.

Почему именно рестарт, а не бой на 60 игроках

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

Поэтому если сервер стабильно падает именно при перезапуске (а не спонтанно посреди сессии), в 90% случаев причина — одна из трёх: повреждённая база персистентности, рассинхрон версий модов после автообновления, или сам скрипт рестарта убивает процесс неправильно. Дальше — по порядку, с тем, как отличить одно от другого по логам.

СимптомСкорее всего причинаГде смотреть
Краш сразу на загрузке миссии, упоминание .bin/persistence в логеБитый storage_1/storage_2profiles/script_*.log, profiles/*.RPT
Краш с текстом Addon '...' requires addon '...' или CRC mismatchКонфликт версий модов после апдейтаprofiles/*.RPT, версии в steamapps/workshop
Процесс просто не запускается, но логов нет вовсеПроблема в restart-скрипте/cron, а не в игреjournalctl -u dayzserver, вывод cron-задачи
Сервер стартует, но откатывает базы/тайники игроковВосстановление со старого storage после сбояprofiles/script_*.log за момент предыдущего краша

storage_1 и storage_2: как DayZ бережёт (и портит) базу предметов

Персистентные данные — построенные базы, тайники, брошенный лут, транспорт — DayZ хранит не в одном файле, а поочерёдно пишет в две папки внутри директории миссии, например ~/dayzserver/mpmissions/dayzOffline.chernarusplus/storage_1/ и storage_2/. Это защита от обрыва записи: пока идёт сохранение в одну копию, вторая остаётся целой на случай сбоя.

Проблема в том, что эта защита работает только при штатной остановке. Если сервер убить в момент, когда обе копии оказались в промежуточном состоянии (что как раз вероятнее при жёстком килле, а не при штатном завершении), при следующем старте движок либо не сможет распарсить данные, либо восстановит версию с явными потерями — пропавшие тайники, откатившиеся базы, задвоенный лут. В логе script_*.log в такой ситуации обычно видна ошибка чтения persistence-файлов или предупреждение о повреждённой базе сразу после строчки инициализации миссии.

Что делать на месте, если сервер уже не стартует:

  1. Остановите сервис и сделайте копию обеих папок, прежде чем что-то трогать: cp -r storage_1 storage_1.bak && cp -r storage_2 storage_2.bak.
  2. Попробуйте временно переименовать повреждённую копию (обычно это та, что новее по времени модификации — ls -la storage_1 storage_2) и запустить сервер на второй.
  3. Если обе копии битые — остаётся принять потерю персистентности и запустить сервер с чистой базой (движок создаст новую при отсутствии обеих папок).

Профилактика надёжнее лечения: держите отдельный бэкап storage_1/storage_2 перед каждым плановым рестартом, а не только перед патчами. Это буквально одна строчка rsync в скрипте рестарта, которая много раз спасала базы игроков.

Поднять сервер DayZ за пару минут

Готовый образ MAATRIX GAMES: NVMe, AMD EPYC, DDoS-защита, панель управления. Локации UK, US, RU. Оплата картой РФ, СБП или криптой.

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

Конфликт модов после автообновления через SteamCMD

Если моды у вас обновляются автоматически (например, отдельным cron-заданием со SteamCMD, которое тянет свежие версии из Workshop независимо от рестарта игры), возникает классическая гонка: SteamCMD может докачать новую версию мода в момент, когда сервер уже запускается или ещё не успел полностью остановиться. Результат — файлы мода частично новые, частично старые, и движок падает с ошибкой вида Addon '@ModName' requires addon '@Dependency' или молчаливым несовпадением подписи (.bikey).

Отдельная и более частая причина: автор мода выкатил обновление в Workshop, SteamCMD его подтянул, а зависимость @CF (Community Framework, от которого зависит большинство крупных модов) осталась на старой сборке. Проверить версию мода можно по времени последнего обновления в манифесте:

cat ~/steamcmd/steamapps/workshop/appworkshop_221100.acf | grep -A2 "1559212036"

Порядок диагностики:

# смотрим последний краш в RPT-логе
tail -100 ~/dayzserver/profiles/*.RPT

# сверяем список модов в ExecStart с реально скачанными папками
ls ~/dayzserver/@*

# проверяем, не идёт ли параллельно ещё одна закачка SteamCMD
ps aux | grep steamcmd

Чтобы разорвать гонку, разделите обновление модов и рестарт сервера по времени с запасом, и не запускайте +workshop_download_item для нескольких модов параллельно из разных процессов — SteamCMD не всегда корректно обрабатывает конкурентные закачки в одну директорию. Подробно о том, откуда брать моды и как считать зависимости, — в статье про установку модов на DayZ-сервер, а сборки, где такие конфликты всплывают чаще всего из-за числа зависимостей, — в обзоре популярных модпаков Namalsk, Deer Isle и других.

Restart-скрипт и cron: где чаще всего косячат

Самая банальная и при этом самая частая причина краша на рестарте — сам скрипт, который его выполняет. Три типичные ошибки:

  • Жёсткий kill вместо штатной остановки. Скрипт вида pkill -9 DayZServer убивает процесс мгновенно, не давая движку дописать persistence — именно так чаще всего и получаются битые storage_1/storage_2 из предыдущего раздела.
  • Гонка с обновлением. Cron-задача на рестарт и cron-задача на app_update через SteamCMD стоят слишком близко по времени и иногда пересекаются — сервер стартует поверх ещё не докачанных файлов.
  • Нет блокировки от повторного запуска. Если предыдущий рестарт завис (например, SteamCMD ждёт ввода или сеть подвисла), а cron уже запустил следующий по расписанию — получаются два конкурирующих процесса, которые дерутся за одни и те же файлы миссии.

Рабочий вариант скрипта с блокировкой и паузой между остановкой и обновлением:

#!/bin/bash
# /opt/scripts/dayz-restart.sh
LOCKFILE="/tmp/dayz-restart.lock"

if [ -e "$LOCKFILE" ]; then
  echo "Предыдущий рестарт ещё не завершён, выходим"
  exit 1
fi
trap "rm -f $LOCKFILE" EXIT
touch "$LOCKFILE"

# бэкап persistence перед остановкой
rsync -a ~/dayzserver/mpmissions/dayzOffline.chernarusplus/storage_1/ ~/backups/storage_1_$(date +%F)/
rsync -a ~/dayzserver/mpmissions/dayzOffline.chernarusplus/storage_2/ ~/backups/storage_2_$(date +%F)/

sudo systemctl stop dayzserver
sleep 5

# обновление сервера и модов — только после полной остановки
~/steamcmd/steamcmd.sh +login anonymous +force_install_dir ~/dayzserver +app_update 223350 +quit

sudo systemctl start dayzserver

И соответствующая cron-запись с логированием вывода, без которого при сбое вы не увидите вообще ничего:

0 5 * * * /opt/scripts/dayz-restart.sh >> /var/log/dayz-restart.log 2>&1

Про общий подход к предупреждению игроков и выбору времени рестарта — в статье про автоматический рестарт сервера по расписанию через cron; там же разобрана логика TimeoutStopSec в systemd-юните, актуальная и для DayZ.

Graceful shutdown вместо kill процесса

У DayZ, в отличие от Rust или Minecraft, нет встроенной команды сохранения через RCON в общем доступе — из коробки штатный способ остановить сервер корректно — отправить ему сигнал SIGTERM, который посылает systemctl stop по умолчанию, а не SIGKILL. Движок перехватывает SIGTERM и успевает дописать persistence и закрыть сокеты; SIGKILL (то же самое, что kill -9 или pkill -9) обрывает процесс мгновенно, независимо от того, на каком этапе записи он находится.

Если у вас установлен BEC (BattlEye Extended Controls) или аналогичный инструмент для админки Arma-движка, через него можно послать команды #shutdown или #restart по BattlEye RCON — это ещё более мягкий путь, чем сигнал ОС, потому что команда идёт через штатный протокол игры, а не снаружи процесса. Для небольших серверов без BEC вполне достаточно правильно настроенного systemd-юнита:

# /etc/systemd/system/dayzserver.service
[Unit]
Description=DayZ Dedicated Server
After=network.target

[Service]
User=steam
WorkingDirectory=/home/steam/dayzserver
ExecStart=/home/steam/dayzserver/DayZServer -config=serverDZ.cfg -port=2302 -profiles=profiles -dologs -adminlog -netlog -freezecheck
Restart=on-failure
RestartSec=10
TimeoutStopSec=60

[Install]
WantedBy=multi-user.target

TimeoutStopSec=60 — ключевая строка: она даёт процессу минуту на корректное завершение после SIGTERM, и только если он не уложился, systemd досылает SIGKILL принудительно. На Windows-хостинге аналог — не форсировать taskkill /F, а сначала попробовать закрыть процесс штатно (через taskkill без /F или через диспетчер задач с ожиданием), форс — только если завис.

Чек-лист и восстановление после краша

Если рестарт всё-таки закончился крашем, порядок действий такой:

  1. Смотрим свежий profiles/script_*.log и profiles/*.RPT — там почти всегда есть конкретная строка с причиной (persistence, addon dependency, отсутствующий файл миссии).
  2. Проверяем, не идёт ли параллельно процесс SteamCMD (ps aux | grep steamcmd) — если да, ждём его завершения, не перезапускаем сервер поверх.
  3. Если ошибка про persistence — откатываемся на бэкап storage_1/storage_2, сделанный перед рестартом (см. скрипт выше).
  4. Если ошибка про моды — сверяем версии в -mod= из ExecStart с реально скачанными папками @ModName и при необходимости передокачиваем битый мод целиком, а не патчим поверх.
  5. После восстановления — запуск не через cron, а вручную с полным выводом в терминал (sudo systemctl start dayzserver && journalctl -u dayzserver -f), чтобы сразу увидеть, поднялся ли процесс до конца.

Чек-лист перед каждым плановым рестартом, чтобы краш вообще не случился:

  • Бэкап storage_1 и storage_2 сделан до остановки, не после.
  • Обновление модов и обновление сервера не пересекаются по времени с самим рестартом (есть запас минимум в несколько минут).
  • В restart-скрипте нет kill -9/pkill -9 — только systemctl stop или явный SIGTERM.
  • Есть lock-файл, защищающий от повторного запуска скрипта, если предыдущий рестарт завис.
  • Вывод cron-задачи логируется в файл, а не теряется молча.

Поднять сервер DayZ за пару минут

Готовый образ MAATRIX GAMES: NVMe, AMD EPYC, DDoS-защита, панель управления. Локации UK, US, RU. Оплата картой РФ, СБП или криптой.

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

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

Можно ли включить автоматическое восстановление из storage_2, если storage_1 повреждён?

Штатного автоматического переключения нет — если основная копия читается с ошибкой, движок может либо упасть, либо создать новую пустую базу. Ручное переименование папок, описанное выше, работает надёжнее, чем надежда на автоматику.

Помогает ли validate в app_update от битых модов?

Помогает от повреждённых файлов самого сервера (движка), но не от рассинхрона версий модов — Workshop-моды скачиваются и валидируются отдельной командой workshop_download_item, и validate на app_update 223350 их не трогает.

Что делать, если краш повторяется на каждом рестарте, а логи не помогают?

Запустите сервер вручную в foreground-режиме (./DayZServer -config=... без демонизации через systemd) — так вывод в терминал часто подробнее, чем то, что попадает в RPT-файл, особенно на самых ранних этапах инициализации.

Обязательно ли делать бэкап persistence перед каждым рестартом, если он ежедневный?

Строго рекомендуется — это дешёвая операция (папки storage_1/storage_2 обычно весят не так много), а цена восстановления без бэкапа — полная потеря баз и тайников всех игроков.

Может ли обновление самой игры (не мода) сломать совместимость с текущими модами?

Да, это отдельная и частая причина краша именно после патча DayZ — крупные апдейты меняют API, и моды, которые их автор ещё не обновил под новую версию, начинают падать при загрузке. В таком случае помогает откат сервера на предыдущую версию через -beta в SteamCMD, если разработчик держит старые билды в депо.