MAATRIX / Блог / Git-деплой на сервер: bare-репозиторий и хуки

Git-деплой на сервер: bare-репозиторий и хуки

Блог MAATRIX · 2026-07-07

Деплой по git push — самый простой способ выкатывать код на один-два сервера без CI-платформ. Пушите в bare-репозиторий на VPS, хук post-receive раскладывает файлы в каталог сайта и перезапускает сервис. Всё нативным git, без сторонних сервисов.

Как это устроено

['На сервере создаётся bare-репозиторий (без рабочего дерева — только история). Локально вы добавляете его как remote и пушите. При получении пуша срабатывает хук post-receive, который делает git checkout рабочего дерева в отдельный каталог и запускает нужные команды — установку зависимостей, миграции, рестарт.', 'Схема идеальна для VPS: никаких раннеров и агентов, только SSH и git, которые уже есть. На root-доступе MAATRIX всё это настраивается за пять минут и не тянет за собой облачную инфраструктуру — весь процесс живёт на одном сервере и полностью под вашим контролем.', 'Стоит понимать разницу между bare и обычным репозиторием. Обычный репозиторий содержит и историю (папку .git), и рабочие файлы, которые вы редактируете. Bare-репозиторий (флаг --bare) хранит только историю, без рабочего дерева — принимать пуши в него безопасно, потому что здесь нет открытых файлов, которые пуш мог бы затереть. Именно поэтому bare-репозиторий выступает точкой приёма, а рабочие файлы хук выкладывает отдельно в каталог сайта.']

Нужен сервер под эту задачу?

Разверните VPS MAATRIX за пару минут: NVMe, AMD EPYC, root-доступ, локации UK и США. Оплата картой РФ и по СБП.

Арендовать VPS для деплоя

Шаг 1. Пользователь и bare-репозиторий

['Заводим отдельного пользователя для деплоя и bare-репозиторий в его домашней директории:', "
sudo adduser --disabled-password --gecos '' deploy\nsudo -u deploy git init --bare /home/deploy/myapp.git
", 'Скопируйте свой SSH-ключ пользователю deploy, чтобы пушить без пароля:', '
ssh-copy-id -i ~/.ssh/id_ed25519.pub deploy@SERVER_IP
', 'Создаём каталог, куда будет выкладываться сайт:', '
sudo mkdir -p /var/www/myapp\nsudo chown deploy:deploy /var/www/myapp
']

Шаг 2. Хук post-receive

['В bare-репозитории создаём исполняемый хук. Он раскладывает содержимое ветки main в каталог сайта:', '
cat > /home/deploy/myapp.git/hooks/post-receive <<\'EOF\'\n#!/bin/bash\nset -e\nTARGET="/var/www/myapp"\nBRANCH="main"\nwhile read oldrev newrev ref; do\n  if [[ "$ref" = "refs/heads/$BRANCH" ]]; then\n    echo "Deploying $BRANCH -> $TARGET"\n    git --work-tree="$TARGET" --git-dir=/home/deploy/myapp.git checkout -f "$BRANCH"\n    cd "$TARGET"\n    # пример: зависимости и рестарт\n    # /opt/myapp/venv/bin/pip install -r requirements.txt\n    sudo systemctl restart myapp\n  fi\ndone\nEOF\nchmod +x /home/deploy/myapp.git/hooks/post-receive
', 'Разберём логику хука. Переменные oldrev, newrev и ref git передаёт в stdin для каждой обновлённой ссылки. Мы реагируем только на ветку main: остальные ветки (feature-, dev-) можно пушить на сервер, не выкатывая их в продакшен. Флаг -f у checkout принудительно приводит рабочее дерево к состоянию ветки, перезаписывая локальные изменения в каталоге сайта — это ожидаемо, каталог сайта не место для ручных правок.', 'После checkout идёт блок деплой-команд. Здесь уместно поставить зависимости, применить миграции БД, собрать фронтенд и в конце перезапустить сервис. Держите этот блок идемпотентным: он выполняется на каждый пуш, поэтому команды должны корректно отрабатывать и на чистом, и на уже развёрнутом приложении.', 'Чтобы хук мог рестартовать сервис без пароля, разрешите deploy запускать конкретную команду через sudoers:', "
echo 'deploy ALL=(root) NOPASSWD: /bin/systemctl restart myapp' | sudo tee /etc/sudoers.d/deploy-myapp\nsudo chmod 440 /etc/sudoers.d/deploy-myapp
"]

Шаг 3. Пуш с локальной машины

['Добавляем сервер как remote и пушим:', '
git remote add production deploy@SERVER_IP:/home/deploy/myapp.git\ngit push production main
', 'Каждый следующий git push production main выкатывает свежий код. Вывод хука (echo Deploying...) вы видите прямо в терминале во время пуша — это ваш живой лог деплоя. Если хук упадёт (set -e прерывает выполнение на первой ошибке), git покажет ненулевой код возврата, и вы сразу поймёте, что выкатка не прошла, ещё не отходя от терминала.', 'Для команды удобно, что remote можно назвать осмысленно: production, staging, backup. Тогда выкатка на разные окружения — это разные bare-репозитории на сервере и разные remote локально, а команда пуша остаётся такой же простой.']

Откат и частые ошибки

['Откат — это просто пуш нужного коммита в main:', '
git push production <commit-hash>:main --force
', '
  • hook not executed — забыли chmod +x на post-receive.
  • checkout: невозможно — не создан TARGET-каталог или у deploy нет прав на запись в него.
  • sudo: a password is required в хуке — неверная строка в sudoers, команда в ней должна совпадать посимвольно с вызовом.
  • Пушите не в ту ветку — хук реагирует только на main.
']

Нужен сервер под эту задачу?

Разверните VPS MAATRIX за пару минут: NVMe, AMD EPYC, root-доступ, локации UK и США. Оплата картой РФ и по СБП.

Арендовать VPS для деплоя

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

Чем это лучше rsync или scp?

Git хранит историю: видно, что и когда выкатили, и откат делается пушем. rsync копирует состояние без версий.

Нужен ли отдельный пользователь deploy?

Не обязательно, но так безопаснее: деплой изолирован от вашего основного аккаунта и имеет только нужные права.

Как деплоить сразу зависимости и миграции?

Впишите команды в тело хука post-receive после checkout — pip install, npm ci, миграции БД, сборку фронтенда.