Автодеплой по webhook: push в GitHub обновляет сервер
Каждый раз заходить по SSH и делать git pull — скучно и легко забыть. Настроим webhook: GitHub при каждом push дёргает наш сервер, тот проверяет подпись и сам обновляет код. Всё на чистом bash и systemd, без тяжёлых CI-платформ.
Содержание
Обсудить статью, задать вопрос или начать новую тему
Есть вопрос по этой статье, идея для обсуждения или просто хотите поделиться опытом? Сообщество MAATRIX ждёт. Для общения, пожалуйста, зарегистрируйтесь в нашем личном кабинете.
Перейти в сообщество →Как работает webhook-деплой
Webhook — это HTTP-запрос, который GitHub отправляет на указанный URL при событии (обычно push). На сервере крутится маленький приёмник, который ловит этот запрос, проверяет, что он действительно от GitHub, и запускает скрипт деплоя: git pull, сборка, перезапуск сервиса.
- Быстро — код на сервере через секунды после push.
- Просто — не нужен Jenkins или GitLab Runner для маленького проекта.
- Безопасно — проверка HMAC-подписи отсекает чужие запросы.
Соберём приёмник на популярной утилите webhook (adnanh/webhook) — это один бинарник на Go, который слушает порт и запускает команды по правилам из YAML.
Нужен сервер под эту задачу?
Разверните VPS MAATRIX за пару минут: NVMe, AMD EPYC, root-доступ, локации UK, США и РФ. Оплата картой РФ и по СБП.
Арендовать VPS для автодеплояСтавим приёмник webhook
В репозиториях Debian/Ubuntu есть готовый пакет, но свежее ставить бинарником с GitHub. Пакетный вариант проще всего.
sudo apt update && sudo apt -y install webhook
Проверим, что бинарник на месте.
webhook -version
Подготовим каталог проекта: клонируем репозиторий туда, где живёт сайт, чтобы деплой сводился к обновлению этой копии.
sudo mkdir -p /var/www/app
sudo chown $USER:$USER /var/www/app
git clone git@github.com:user/repo.git /var/www/app
Скрипт деплоя
Логику обновления вынесем в отдельный скрипт — так его легко тестировать вручную. Он подтягивает изменения и перезапускает сервис приложения.
sudo tee /var/www/deploy.sh > /dev/null <<'EOF'
#!/usr/bin/env bash
set -euo pipefail
cd /var/www/app
git fetch --all
git reset --hard origin/main
# npm ci && npm run build # при необходимости
sudo systemctl restart app.service
echo "Deployed $(git rev-parse --short HEAD)"
EOF
sudo chmod +x /var/www/deploy.sh
git reset --hard origin/main вместо git pull надёжнее: он гарантирует, что рабочая копия точно совпадает с веткой, даже если на сервере кто-то случайно наследил в файлах.
Правило webhook с проверкой подписи
GitHub подписывает каждый запрос секретом через HMAC-SHA256 в заголовке X-Hub-Signature-256. Приёмник обязан проверить эту подпись — иначе любой сможет дёргать деплой. Опишем правило в JSON.
sudo tee /etc/webhook.conf > /dev/null <<'EOF'
[
{
"id": "deploy",
"execute-command": "/var/www/deploy.sh",
"command-working-directory": "/var/www/app",
"trigger-rule": {
"and": [
{ "match": { "type": "payload-hmac-sha256",
"secret": "MY_WEBHOOK_SECRET",
"parameter": { "source": "header", "name": "X-Hub-Signature-256" } } },
{ "match": { "type": "value", "value": "refs/heads/main",
"parameter": { "source": "payload", "name": "ref" } } }
]
}
}
]
EOF
Второе условие match value refs/heads/main запускает деплой только на push в ветку main, игнорируя фичевые ветки.
Запуск через systemd
Чтобы приёмник жил после перезагрузки, обернём его в systemd-юнит. Слушать будем локальный порт, наружу проксировать через Nginx с TLS.
sudo tee /etc/systemd/system/webhook.service > /dev/null <<'EOF'
[Unit]
Description=GitHub webhook receiver
After=network.target
[Service]
ExecStart=/usr/bin/webhook -hooks /etc/webhook.conf -port 9000 -verbose
Restart=always
[Install]
WantedBy=multi-user.target
EOF
sudo systemctl daemon-reload
sudo systemctl enable --now webhook
В настройках репозитория на GitHub (Settings → Webhooks) укажи Payload URL вида https://srv.example.com/hooks/deploy, Content type — application/json, Secret — тот же MY_WEBHOOK_SECRET. Проксируй /hooks/ в Nginx на http://127.0.0.1:9000.
Безопасность и частые ошибки
- Секрет обязателен — без проверки подписи webhook открыт для любого, кто узнает URL.
- Не запускай деплой от root — заведи отдельного пользователя с правами только на каталог проекта.
- Логируй — флаг
-verboseиjournalctl -u webhookпомогут понять, почему деплой не сработал. - Таймаут на сборку — тяжёлый
npm run buildможет подвесить приёмник; выноси долгие задачи в фоновый systemd-run.
Проверить связку целиком просто: сделай тестовый push и посмотри логи. GitHub на вкладке Recent Deliveries показывает код ответа — 200 значит, что подпись прошла и скрипт запустился.
journalctl -u webhook -f
Нужен сервер под эту задачу?
Разверните VPS MAATRIX за пару минут: NVMe, AMD EPYC, root-доступ, локации UK, США и РФ. Оплата картой РФ и по СБП.
Арендовать VPS для автодеплояОбсудить статью, задать вопрос или начать новую тему
Есть вопрос по этой статье, идея для обсуждения или просто хотите поделиться опытом? Сообщество MAATRIX ждёт. Для общения, пожалуйста, зарегистрируйтесь в нашем личном кабинете.
Перейти в сообщество →Частые вопросы
Чем это лучше GitHub Actions?
Actions гоняет сборку в облаке и потом всё равно должен как-то попасть на сервер. Webhook-деплой обновляет сервер напрямую и не зависит от лимитов минут CI — для небольших проектов это проще и быстрее.
Что если сборка упала?
Скрипт с set -euo pipefail прервётся на ошибке и не перезапустит сервис с битым кодом. Стоит добавить откат на предыдущий коммит при неудачной сборке.
Можно ли деплоить без публичного порта?
Да — вместо приёмного webhook используй pull-модель: cron или GitHub Actions с self-hosted runner, который сам ходит за изменениями. Но входящий webhook быстрее реагирует на push.