Сервер Valheim на Linux стоит запускать под systemd: один unit-файл /etc/systemd/system/valheim.service, который запускает сервер от отдельного пользователя, стартует его при загрузке системы, перезапускает после сбоя и - строка, о которой чаще всего забывают - останавливает через KillSignal=SIGINT, чтобы мир сохранился при выходе. Добавьте TimeoutStopSec=120, чтобы большой мир успел дописаться на диск, читайте лог через journalctl -u valheim и используйте таймеры systemd вместо cron для еженедельного рестарта и ночного бэкапа. В этой статье unit собирается построчно, объясняется, от чего защищает каждая настройка, и разбираются типичные проблемы: циклы падений, остановка, которая превращается в kill, пропавшие логи и несколько серверов на одной машине.
Предполагается, что сервер уже установлен через SteamCMD под пользователем valheim в /home/valheim/server, а запускается собственным скриптом. Если это не так, статья установка сервера Valheim на Linux через SteamCMD доведёт вас до этого состояния минут за пятнадцать, а здесь мы продолжаем с того места, где заканчивается её раздел про systemd.
Почему systemd, а не tmux или screen#
Для сервера, который работает месяцами, должны выполняться три условия: он поднимается после перезагрузки, поднимается после сбоя и корректно останавливается по вашей команде. Процесс, запущенный из SSH-сессии, умирает вместе с ней. tmux и screen удерживают его живым после отключения, поэтому их так любят в руководствах, но ни один не запускает ничего при загрузке и ни один не замечает, что процесс завершился. О падении сервера вы узнаёте, когда вам кто-нибудь напишет.
systemd закрывает все три пункта и добавляет то, чего терминальный мультиплексор не даст никогда:
- Управляемая остановка. systemd отправляет выбранный вами сигнал, ждёт столько, сколько вы разрешили, и только потом усиливает меры. С правильным сигналом каждая плановая остановка - это сохранённая остановка.
- Лог, по которому можно искать. Всё, что печатает сервер, попадает в журнал с метками времени, переживает перезапуски и фильтруется по времени и шаблону.
- Политика перезапуска с тормозом. Сервер перезапускается при сбое, но systemd сдаётся, если он падает в тесном цикле, вместо того чтобы мучить диск сломанным миром.
- Ограничения ресурсов. Потолок памяти, вес CPU и ограничения файловой системы - по одной строке на каждое.
- Таймеры. Запланированные рестарты и бэкапы, которые попадают в лог, видны в одном списке и могут зависеть от самой службы.
Теряется интерактивная консоль. Но выделенный сервер Valheim всё равно не читает команды из стандартного ввода - админские команды вводятся в консоли F5 игрового клиента игроком из adminlist.txt - так что в этой игре потеря почти нулевая. Если вам нужна подключаемая консоль по другим причинам, это главный довод в пользу LinuxGSM, который запускает сервер внутри tmux и оборачивает его собственными инструментами.
Скрипт запуска, который вызывает unit#
systemd должен запускать скрипт, а не бинарник напрямую, потому что серверу нужны две переменные окружения и рабочий каталог. Не указывайте в unit штатный start_server.sh: SteamCMD восстанавливает его при каждом обновлении и validate, и ваши аргументы пропадают после очередного патча. Держите собственную копию:
#!/bin/bashcd "$(dirname "$0")"export LD_LIBRARY_PATH=./linux64:$LD_LIBRARY_PATHexport SteamAppId=892970exec ./valheim_server.x86_64 -nographics -batchmode \ -name "Longship Crew" -port 2456 -world "Midgard" \ -password "herring-barrel-42" -public 1 \ -saveinterval 900 -backups 4Для systemd важна деталь exec. Без неё скрипт работает как процесс оболочки, а сервер - его дочерний процесс, и сигнал, который systemd шлёт при остановке, попадает в оболочку. Bash не пересылает сигналы фоновому дочернему процессу так, как вы могли бы ожидать, поэтому сервер может вообще пропустить сигнал штатной остановки и быть убит по таймауту. С exec оболочка заменяет сама себя на valheim_server.x86_64, сервер становится главным процессом службы, и сигнал уходит прямо ему.
Каждый аргумент в этой строке разобран в справочнике аргументов запуска Valheim. Два из них стоит выбрать осознанно для службы: -saveinterval (по умолчанию 1800 секунд) ограничивает, сколько вы потеряете при сбое, и -logFile, который лучше не указывать, если вы хотите видеть лог в журнале.
Unit-файл построчно#
[Unit]Description=Valheim dedicated serverWants=network-online.targetAfter=network-online.targetStartLimitIntervalSec=600StartLimitBurst=4[Service]Type=simpleUser=valheimGroup=valheimWorkingDirectory=/home/valheim/serverExecStart=/home/valheim/server/start_valheim.shKillSignal=SIGINTTimeoutStopSec=120Restart=on-failureRestartSec=15[Install]WantedBy=multi-user.target$ sudo systemctl daemon-reload$ sudo systemctl enable --now valheim$ systemctl status valheimЗачем нужна каждая строка:
| Строка | Зачем она нужна |
|---|---|
Wants= / After=network-online.target | Запуск после поднятия сети, чтобы регистрация в Steam не падала при загрузке |
StartLimitIntervalSec=600, StartLimitBurst=4 | Больше четырёх запусков за десять минут - и systemd перестаёт пытаться |
Type=simple | Скрипт остаётся на переднем плане; systemd отслеживает его напрямую |
User= / Group= | Никогда не запускайте игровой сервер от root |
WorkingDirectory= | Путь к библиотекам в скрипте задан относительно каталога установки |
KillSignal=SIGINT | systemctl stop работает как Ctrl+C, а это сохраняет мир |
TimeoutStopSec=120 | Сколько systemd ждёт сохранения, прежде чем отправит SIGKILL |
Restart=on-failure, RestartSec=15 | Подняться после сбоя, предварительно выдержав паузу |
WantedBy=multi-user.target | К чему привязывается enable, чтобы служба стартовала при загрузке |
В актуальных версиях systemd настройки лимита запусков находятся в [Unit]. Старые релизы принимали StartLimitInterval= в [Service]; если systemctl status предупреждает о неизвестном ключе, проверьте в man systemd.unit на своей машине, как он пишется там.
Чистая остановка: SIGINT, таймауты и сохранение#
Эта часть unit решает, будет ли перезагрузка вам чего-то стоить. Valheim держит мир в памяти и записывает на диск каждые -saveinterval секунд. Корректное завершение записывает его ещё раз напоследок; всё остальное - нет.
Когда вы выполняете systemctl stop valheim с приведённым выше unit, происходит следующее:
- systemd отправляет
SIGINTглавному процессу - то есть серверу, благодаряexec. - Сервер пишет в лог, что завершает работу, сохраняет мир и выходит. На небольшом мире это занимает секунду-две; на старом мире с большими базами - заметно дольше, а медленные диски растягивают это ещё сильнее.
- Если процесс всё ещё работает после
TimeoutStopSec, systemd отправляетSIGKILLи записывает, что остановка завершилась по таймауту.
Проверьте это один раз намеренно, прежде чем полагаться на настройку:
$ sudo systemctl stop valheim$ journalctl -u valheim -n 40 --no-pagerВ последних строках вывода перед завершением процесса вы должны увидеть сохранение мира, а от systemd - обычное Stopped Valheim dedicated server. Если вместо этого вы видите State 'stop-sigterm' timed out. Killing. или Main process exited, code=killed, status=9/KILL, сервер не успел завершиться или вообще не получил сигнал. Проверьте по порядку: что KillSignal=SIGINT есть в unit и что после его добавления вы выполнили daemon-reload; что в скрипте есть exec; что TimeoutStopSec достаточно велик для вашего мира.
systemctl restart - это остановка, за которой следует запуск, так что остановка тоже чистая. systemctl kill valheim такой не даёт - по умолчанию он отправляет SIGTERM каждому процессу в unit - как и OOM-killer ядра, отключение питания или сбой хоста. Во всех этих случаях защитой служат только короткий интервал сохранения и бэкапы, лежащие в другом месте. Как выглядит недописанный мир и как из него выбраться, описано в статье бэкапы сервера Valheim и повреждение мира.
Политика перезапуска и циклы падений#
Restart=on-failure перезапускает сервер, когда он завершается с ненулевым статусом, убит неожиданным сигналом или превысил таймаут, - и не перезапускает после systemctl stop, что вам и нужно. Альтернативы:
| Политика | Перезапуск после | Когда использовать |
|---|---|---|
no | Ничего | Вы хотите разбирать каждый выход вручную |
on-failure | Сбои, kill, ненулевые коды выхода | Обычный выбор для игрового сервера |
on-abnormal | Сигналы и таймауты, но не ненулевые коды выхода | Сервер по какой-то причине намеренно завершается с ненулевым кодом |
always | Всё, включая штатный выход | Здесь редко подходит; намеренное выключение из игры сразу возвращается обратно |
RestartSec=15 - пауза перед каждым перезапуском. Она даёт время освободиться UDP-портам старого процесса и не позволяет циклу падений крутиться на полной скорости.
Тормозом служит лимит запусков. Если файл мира повреждён или обновление игры сломало мод BepInEx, сервер будет падать при каждом старте. Без лимита systemd перезапускает его бесконечно, журнал заполняется одной и той же ошибкой тысячи раз, и первое, самое полезное её появление оказывается погребено. При StartLimitBurst=4 за десять минут systemd помечает unit как неудавшийся и останавливается:
$ systemctl status valheim# Active: failed (Result: start-limit-hit)$ journalctl -u valheim -b --no-pager | tail -n 80$ sudo systemctl reset-failed valheim$ sudo systemctl start valheimПрочитайте лог, прежде чем что-либо сбрасывать. У типичных сбоев при запуске - отсутствующий steamclient.so, занятый порт, ошибка препроцессора BepInEx, мир, который не загружается - есть узнаваемые строки, и все они перечислены в статье сервер Valheim падает при запуске.
Логи в journalctl#
Без аргумента -logFile сервер печатает в стандартный вывод, systemd его перехватывает, и журнал становится логом сервера. Команды, которые вам действительно пригодятся:
# follow the log live$ journalctl -u valheim -f# everything since the current boot$ journalctl -u valheim -b# a time window$ journalctl -u valheim --since "2026-10-14 19:00" --until "2026-10-14 23:00"# search: connections, the join code, errors$ journalctl -u valheim -g "Got connection|join code|Exception"# the last 200 lines without the pager$ journalctl -u valheim -n 200 --no-pagerЛог Valheim многословен, а в некоторых дистрибутивах журнал хранится только в памяти и исчезает при перезагрузке. Если каталога /var/log/journal нет, задайте Storage=persistent в /etc/systemd/journald.conf и перезапустите systemd-journald. Чтобы журнал не рос бесконечно, задайте в том же файле SystemMaxUse= - для одного игрового сервера хватит 500M - или проверьте текущий объём командой journalctl --disk-usage.
Другой вариант - файл: добавьте -logFile /home/valheim/valheim.log в строку запуска, и сервер будет писать туда вместо журнала. Это удобно тем, кто любит tail и grep на обычном файле, но тогда ротация лежит на вас - через logrotate с copytruncate, потому что сервер держит файл открытым. Выберите что-то одно: разделение лога между двумя местами усложняет любое расследование. Что означают строки - запуск, код подключения, соединения, ошибки, на которые стоит реагировать, - описано в статье лог сервера Valheim: разбор.
Обновления: при каждом старте или вручную#
Приведённый выше unit игру не обновляет. Заставить его это делать можно одной дополнительной строкой в [Service]:
ExecStartPre=/usr/games/steamcmd +force_install_dir /home/valheim/server +login anonymous +app_update 896660 +quitТогда каждый запуск, включая каждый перезапуск и каждую загрузку системы, сначала проверяет наличие новой сборки сервера. Для ванильного сервера это удобно: когда выходит патч Valheim и игроки видят Incompatible version, достаточно systemctl restart valheim.
Для модифицированного сервера это неверный выбор. Патч игры обычно ломает моды BepInEx, пока их авторы не подтянутся, а автоматическое обновление означает, что рестарт в 5 утра может оставить вас с сервером, который падает в цикле, пока кто-нибудь не заметит. На модифицированном сервере не добавляйте ExecStartPre и обновляйтесь осознанно: остановите сервер, сделайте бэкап, запустите SteamCMD вручную от пользователя valheim, убедитесь, что ваши моды обновлены, запустите снова. Ещё два небольших замечания: проверка обновления добавляет ко времени каждого запуска время работы SteamCMD, а если обновление не удалось - Steam недоступен, диск полон - ExecStartPre завершается ошибкой, и сервер не запускается вовсе. Поставьте перед путём - (ExecStartPre=-/usr/games/steamcmd ...), чтобы сервер стартовал на старой сборке, если шаг обновления не удался. Вопрос выбора времени для групп разобран в статье обновление сервера Valheim и несовпадение версий.
Таймеры: еженедельные рестарты и ночные бэкапы#
Таймеры systemd делают то же, что и cron, но у них есть два преимущества для игрового сервера: их запуски пишутся в журнал рядом с собственным выводом сервера, а systemctl list-timers показывает, когда сработает каждый из них в следующий раз. Таймер - это пара файлов: .timer, который говорит когда, и .service, который говорит что.
Еженедельный рестарт рано утром в понедельник:
[Unit]Description=Restart the Valheim server[Service]Type=oneshotExecStart=/usr/bin/systemctl restart valheim.service[Unit]Description=Weekly Valheim restart[Timer]OnCalendar=Mon *-*-* 05:00:00[Install]WantedBy=timers.targetНочной бэкап всего каталога сохранений, запускаемый от пользователя valheim:
#!/bin/bashset -euo pipefaildest=/home/valheim/backupsmkdir -p "$dest"tar -czf "$dest/valheim-$(date +%F-%H%M).tar.gz" \ -C /home/valheim/.config/unity3d/IronGate Valheimfind "$dest" -name 'valheim-*.tar.gz' -mtime +14 -delete[Unit]Description=Back up Valheim worlds[Service]Type=oneshotUser=valheimExecStart=/home/valheim/backup_valheim.sh[Unit]Description=Nightly Valheim backup[Timer]OnCalendar=*-*-* 05:10:00Persistent=true[Install]WantedBy=timers.target$ chmod +x /home/valheim/backup_valheim.sh$ sudo systemctl daemon-reload$ sudo systemctl enable --now valheim-restart.timer valheim-backup.timer$ systemctl list-timers 'valheim*'$ systemd-analyze calendar "Mon *-*-* 05:00:00"По понедельникам бэкап запускается через десять минут после рестарта, поэтому в эти дни он архивирует мир, сохранённый за считаные минуты до того. В остальные дни он работает на живом сервере, и архив, снятый в момент сохранения, может захватить недописанный файл. Либо назначьте таймер на время сразу после ежедневного рестарта, либо пусть скрипт копирует самую свежую полную пару из собственной ротации -backups самого Valheim. Persistent=true выполнит пропущенный бэкап при следующей загрузке, если машина была выключена в 05:10; для таймера рестарта этот параметр не включайте: запоздалое наверстывание перезапустит сервер посреди вечера. Учтите, что путь сохранений выше - стандартный; если вы используете -savedir, архивируйте его - все расположения перечислены в статье где Valheim хранит свои файлы.
Бэкап на том же диске, что и мир, защищает только от неудачного сохранения и ни от чего больше. Копируйте архивы ещё и за пределы машины; один из способов показан в статье бэкапы игровых серверов в S3. И помните: на ванильном Valheim у сервера нет игрового оповещения - ни RCON, ни консоли stdin - поэтому предупреждайте игроков об окне рестарта в Discord, а не рассчитывайте на сообщение в игре.
Ограничения, усиление защиты и несколько серверов#
Когда основа заработает, стоит добавить несколько строк в [Service]:
MemoryMax=6GNoNewPrivileges=truePrivateTmp=trueProtectSystem=fullNice=-5MemoryMax= - жёсткий потолок, который обеспечивают cgroups: упрётесь в него, и ядро убьёт сервер без сохранения, что лучше, чем когда вся машина уходит в своп до полной остановки, но всё равно остаётся сбоем. Задавайте значение заметно выше обычного рабочего набора сервера - реалистичные цифры есть в руководстве по RAM для Valheim - или сначала используйте MemoryHigh=, который притормаживает, а не убивает. ProtectSystem=full делает /usr, /boot и /etc доступными службе только для чтения; не добавляйте ProtectHome=, потому что сервер и его сохранения лежат в домашнем каталоге. Nice=-5 даёт серверу небольшой приоритет над другой работой на общей машине.
Чтобы запустить несколько миров на одной машине, превратите unit в шаблон valheim@.service с EnvironmentFile=/etc/valheim/%i.env и строкой запуска, собранной из переменных вроде $NAME, $WORLD, $PORT и $PASSWORD. Каждому экземпляру нужна своя пара портов с разницей минимум в два (2456, затем 2458), своё имя мира и, в идеале, свой -savedir. Запускайте их как valheim@main и valheim@test. Арифметика портов описана в статье порты Valheim 2456-2458: разбор.
Если root вам недоступен вовсе, тот же unit работает как пользовательская служба в ~/.config/systemd/user/, управляемая через systemctl --user. Она переживает выход из системы, только если для этого пользователя включён lingering (loginctl enable-linger valheim, что один раз требует root). Более широкое введение в unit, зависимости и лимиты за пределами Valheim есть в статье службы systemd для ваших приложений.
FAQ#
Сохраняет ли Valheim мир, когда его останавливает systemd?
Да, если unit отправляет SIGINT и сигнал доходит до процесса сервера. Для этого нужны KillSignal=SIGINT в unit и exec в скрипте запуска. С SIGTERM по умолчанию или с оболочкой между systemd и сервером сохранение не гарантировано.
Каким должен быть TimeoutStopSec?
Достаточным для самого большого сохранения плюс запас. 120 секунд - комфортное значение почти для любого мира. Если в журнале когда-нибудь появится остановка по таймауту и SIGKILL, увеличьте его - и проверьте диск, потому что такое медленное сохранение обычно указывает на накопитель.
Почему systemctl показывает start-limit-hit?
Сервер упал больше раз, чем позволяет StartLimitBurst за StartLimitIntervalSec, и systemd перестал пытаться. Прочитайте настоящую ошибку в journalctl -u valheim -b, исправьте её, затем выполните systemctl reset-failed valheim и только после этого запускайте снова.
Что использовать для рестартов: таймер systemd или cron?
Подойдёт любой. Таймеры пишут каждый запуск в журнал, показывают следующий запуск в systemctl list-timers и могут наверстать пропущенные запуски с Persistent=true, что полезно для бэкапов. Cron подходит, если вы уже управляете там всем остальным; синтаксис описан в статье выражения cron: разбор.
Можно ли вводить команды на сервер через systemd?
Толку от этого нет. У выделенного сервера Valheim нет консоли stdin, а в ваниле - и RCON. Админские команды выполняются в консоли F5 игрового клиента игроками из adminlist.txt, и это работает одинаково, как бы ни был запущен сервер.


