Автоматизация деплоя конфигов игрового сервера через git
Знакомая ситуация: правишь server.properties через SFTP прямо на проде, забываешь сохранить копию «на всякий случай», а через неделю выясняется, что кто-то из соадминов перезаписал твои правки своими. Или ещё веселее — обновил конфиг, сервер не поднялся, а какой именно параметр сломал запуск, вспомнить уже не можешь, потому что предыдущая версия файла нигде не сохранена. Git решает это не как экзотика для программистов, а как рабочий инструмент: конфиги живут в репозитории, изменения видно построчно, откат — одна команда, а деплой — обычный git push.
Содержание
Зачем конфигам git, а не просто бэкапы
Папка с архивами server.cfg.bak, server.cfg.bak2, server.cfg_rabochij — это тоже своего рода версионирование, только без метаданных: непонятно, кто менял, зачем и что именно изменилось между версиями. Плюс такие бэкапы почти никогда не почищены — раз в полгода админ находит десяток файлов и не может понять, какой актуальный.
Git закрывает три конкретные проблемы разом:
- История с диффом.
git log -p server.propertiesпокажет каждое изменение построчно с датой, автором и (если приучить себя писать вменяемые сообщения) причиной правки. - Откат одной командой. Не подошёл новый
view-distanceилиtickrate—git revertи повторный деплой возвращают рабочую версию за секунды, а не за раскопки в бэкапах. - Совместная работа без перезаписи. Если конфиги правят несколько админов (см. права доступа и роли администраторов на сервере), git через merge-конфликты честно покажет, где правки пересеклись, вместо того чтобы молча похоронить чужие изменения.
Это не замена полноценным бэкапам мира и сейвов — про них отдельный разговор в статье про автобэкапы игрового сервера. Git здесь именно для конфигурационных файлов: server.properties, server.cfg, bukkit.yml, файлы плагинов — текстовые, небольшие, часто меняющиеся вручную.
Что класть в репозиторий, а что — нет
Структура для парка из нескольких серверов обычно строится по папке на инстанс:
gameserver-configs/
├── minecraft-survival/
│ ├── server.properties
│ ├── bukkit.yml
│ ├── spigot.yml
│ └── plugins/EssentialsX/config.yml
├── rust-eu-1/
│ └── server.cfg
├── cs2-comp-1/
│ └── gamemode_competitive2v2.cfg
└── README.md
Ключевое правило: в репозиторий не идёт ничего, что является секретом или персональными данными игроков. Это RCON-пароли, токены Discord-ботов, ключи API хостинга, whitelist/banlist с реальными данными игроков (см. хранение персональных данных игроков и приватность). Заведи .gitignore:
*.log
world/
worlds/
*rcon*password*
secrets.env
whitelist.json
banned-players.json
Секреты — либо подставляются на сервере из отдельного файла окружения (/etc/gameserver/secrets.env, вне репозитория), либо через шаблонизатор при развёртывании через несколько серверов (если у тебя их уже пять и больше, обрати внимание на Ansible-автоматизацию нескольких игровых серверов — git-деплой из этой статьи и Ansible прекрасно комбинируются: git хранит и версионирует, Ansible раскатывает шаблоны с секретами через Vault).
Нужен игровой сервер?
MAATRIX GAMES — 45+ игр, деплой за минуты, DDoS-защита, автобэкапы. Тарифы от 399 ₽/мес.
Создать серверНастройка bare-репозитория на сервере
Самая надёжная схема — «push для деплоя»: на сервере создаётся голый (bare) репозиторий, который принимает пуш и через хук раскладывает файлы в рабочую директорию сервиса.
На игровом сервере:
mkdir -p /srv/git/gameserver-configs.git
cd /srv/git/gameserver-configs.git
git init --bare
Локально (или в CI) добавляешь его как remote и настраиваешь доступ по SSH-ключу (не по паролю — см. SSH-ключи вместо пароля для нескольких администраторов сервера):
git remote add production deploy@203.0.113.10:/srv/git/gameserver-configs.git
git push production main
Голый репозиторий сам по себе ничего не разворачивает — файлов рабочей копии в нём нет. Разворачивать будет хук post-receive, который сработает при получении пуша.
Хук `post-receive`: деплой и рестарт сервиса
Хук — обычный shell-скрипт hooks/post-receive внутри bare-репозитория. Он не версионируется вместе с конфигами (хуки — часть .git, а не рабочего дерева), поэтому его нужно один раз положить на сервер руками или бутстрап-скриптом при заказе нового узла:
#!/bin/bash
# /srv/git/gameserver-configs.git/hooks/post-receive
set -e
TARGET="/home/deploy/minecraft-survival"
GIT_DIR="/srv/git/gameserver-configs.git"
BRANCH="main"
while read oldrev newrev ref; do
if [ "$ref" = "refs/heads/$BRANCH" ]; then
git --work-tree="$TARGET" --git-dir="$GIT_DIR" checkout -f "$BRANCH" -- minecraft-survival/
echo "[deploy] Конфиги обновлены: $(date -u +%FT%TZ)"
# предупредить игроков перед рестартом, а не просто оборвать сессию
/home/deploy/rcon-cli --host 127.0.0.1 --port 25575 "say Сервер перезапустится через 30 сек для применения конфигов"
sleep 30
systemctl restart minecraft-survival
echo "[deploy] Сервис перезапущен"
fi
done
chmod +x /srv/git/gameserver-configs.git/hooks/post-receive
Про сам RCON-клиент и команды — в статье про подключение по RCON и основные команды. Обрати внимание на checkout -f ... -- minecraft-survival/ — так деплоится только конкретная папка сервера, если в репозитории лежат конфиги сразу нескольких инстансов, а хук привязан к одному TARGET. Для нескольких серверов в одном репозитории проще сделать по хуку на каждый TARGET, либо один скрипт с циклом по всем известным папкам.
Альтернатива push-схеме — pull-based деплой: рабочая копия на сервере сама является обычным (не bare) git-репозиторием, и по cron или вебхуку в ней выполняется git pull, а хук post-merge в .git/hooks/ делает рестарт. Разница чисто организационная: pull удобен, если конфиги правят через веб-интерфейс git-хостинга и мерж-реквесты, push — если хочешь явно и осознанно катить деплой одной командой из терминала.
Пример для разных типов серверов
Механизм одинаковый, но формат конфига и способ рестарта отличаются по игре:
| Игра | Файл конфига | Команда применения |
|---|---|---|
| Minecraft (Paper/Spigot) | server.properties, bukkit.yml | рестарт сервиса (большинство параметров не хот-релоадятся) |
| Rust | server.cfg (у решений на базе Oxide/uMod) | server.writecfg + рестарт для большинства серверных параметров |
| CS2 | gamemode_*.cfg, server.cfg | exec server.cfg через RCON без рестарта, если правился только gameplay-cfg |
| ARK: Survival Ascended | GameUserSettings.ini, Game.ini | рестарт сервиса — конфиг читается при старте |
Для CS2 хук можно упростить — вместо рестарта сервиса отправить RCON-команду exec gamemode_competitive2v2.cfg, если менялся именно исполняемый cfg, а не файлы, требующие полной перезагрузки процесса. Это сильно снижает даунтайм на активных серверах, но работает только для параметров, которые сам движок разрешает применять через exec на лету — часть настроек (например, порт или лимит слотов) всё равно требует рестарта.
Для Windows-хостов (ARK на Windows) post-receive пишется на PowerShell вместо bash — механика git та же самая, различается только сам скрипт хука и команда рестарта службы (Restart-Service).
CI-проверка конфигов перед деплоем
Голая связка «пуш → чекаут → рестарт» опасна тем, что синтаксическая ошибка в конфиге долетит до прода мгновенно и уронит сервис при рестарте. Если конфиги хранятся в GitHub/GitLab и правки идут через pull request, добавь простую проверку до мержа в main:
# .github/workflows/validate-configs.yml
name: validate-configs
on:
pull_request:
branches: [main]
jobs:
lint:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Проверить обязательные ключи server.properties
run: |
grep -q "^motd=" minecraft-survival/server.properties
grep -q "^max-players=" minecraft-survival/server.properties
grep -q "^query.port=" minecraft-survival/server.properties
- name: Проверить YAML-конфиги на синтаксис
run: |
pip install yamllint
yamllint minecraft-survival/bukkit.yml minecraft-survival/spigot.yml
Это не гарантия, что сервер не упадёт (проверить логику игрового движка снаружи нельзя), но отсекает банальные опечатки и пропущенные обязательные поля ещё на этапе pull request, до того как конфиг вообще попадёт в main и уйдёт в деплой. Мерж в main дальше можно триггерить автодеплой через appleboy/ssh-action (выполнить git pull на сервере по SSH) — это уже вариант pull-схемы поверх той же истории в git.
Нужен игровой сервер?
MAATRIX GAMES — 45+ игр, деплой за минуты, DDoS-защита, автобэкапы. Тарифы от 399 ₽/мес.
Создать серверЧастые вопросы
Куда девать RCON-пароли и токены, если их нельзя коммитить?
Держи их в отдельном файле вне репозитория (/etc/gameserver/secrets.env) или в secrets CI/CD-системы, а хук/playbook подставляет их при деплое. В .gitignore добавь маску на такие файлы, чтобы случайно не закоммитить.
Что если кто-то поправил конфиг руками прямо на сервере в обход git?
При следующем checkout -f в хуке эти правки будут молча затёрты — это осознанный компромисс push-схемы. Если такое случается часто, лучше перейти на pull-схему с git status/git diff перед мержем, чтобы дрейф был виден до деплоя, а не после.
Нужен ли отдельный репозиторий на каждую игру?
Для одного-двух серверов удобнее один репозиторий с папками по инстансам, как в примере выше. Когда серверов становится пять и больше и хочется единообразных ролей, переезжай на связку git + Ansible — репозиторий остаётся источником истины, а раскатку по хостам берёт на себя Ansible.
Безопасно ли давать нескольким админам push-доступ к боевому репозиторию?
Да, если у каждого свой SSH-ключ и доступ ограничен нужной директорией на сервере — тогда история покажет, кто и когда внёс правку, что само по себе дисциплинирует лучше устных договорённостей.
А если конфиг сломал запуск сервера после деплоя?
git log --oneline в репозитории покажет последние коммиты, git revert <hash> создаст обратный коммит, повторный git push production main откатит прод к рабочей версии — без ручного восстановления файла из архива.