Автодеплой по webhook: push в GitHub обновляет сервер
Каждый раз заходить по SSH и делать git pull — скучно и легко забыть. Настроим webhook: GitHub при каждом push дёргает наш сервер, тот проверяет подпись и сам обновляет код. Всё на чистом bash и systemd, без тяжёлых CI-платформ.
Содержание
Как работает 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\nsudo chown $USER:$USER /var/www/app\ngit clone git@github.com:user/repo.git /var/www/app']
Скрипт деплоя
['Логику обновления вынесем в отдельный скрипт — так его легко тестировать вручную. Он подтягивает изменения и перезапускает сервис приложения.', 'sudo tee /var/www/deploy.sh > /dev/null <<\'EOF\'\n#!/usr/bin/env bash\nset -euo pipefail\ncd /var/www/app\ngit fetch --all\ngit reset --hard origin/main\n# npm ci && npm run build # при необходимости\nsudo systemctl restart app.service\necho "Deployed $(git rev-parse --short HEAD)"\nEOF\nsudo 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\'\n[\n {\n "id": "deploy",\n "execute-command": "/var/www/deploy.sh",\n "command-working-directory": "/var/www/app",\n "trigger-rule": {\n "and": [\n { "match": { "type": "payload-hmac-sha256",\n "secret": "MY_WEBHOOK_SECRET",\n "parameter": { "source": "header", "name": "X-Hub-Signature-256" } } },\n { "match": { "type": "value", "value": "refs/heads/main",\n "parameter": { "source": "payload", "name": "ref" } } }\n ]\n }\n }\n]\nEOF', 'Второе условие match value refs/heads/main запускает деплой только на push в ветку main, игнорируя фичевые ветки.']
Запуск через systemd
['Чтобы приёмник жил после перезагрузки, обернём его в systemd-юнит. Слушать будем локальный порт, наружу проксировать через Nginx с TLS.', "sudo tee /etc/systemd/system/webhook.service > /dev/null <<'EOF'\n[Unit]\nDescription=GitHub webhook receiver\nAfter=network.target\n\n[Service]\nExecStart=/usr/bin/webhook -hooks /etc/webhook.conf -port 9000 -verbose\nRestart=always\n\n[Install]\nWantedBy=multi-user.target\nEOF\nsudo systemctl daemon-reload\nsudo 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.
journalctl -u webhook -f']
Нужен сервер под эту задачу?
Разверните VPS MAATRIX за пару минут: NVMe, AMD EPYC, root-доступ, локации UK и США. Оплата картой РФ и по СБП.
Арендовать VPS для автодеплояЧастые вопросы
Чем это лучше GitHub Actions?
Actions гоняет сборку в облаке и потом всё равно должен как-то попасть на сервер. Webhook-деплой обновляет сервер напрямую и не зависит от лимитов минут CI — для небольших проектов это проще и быстрее.
Что если сборка упала?
Скрипт с set -euo pipefail прервётся на ошибке и не перезапустит сервис с битым кодом. Стоит добавить откат на предыдущий коммит при неудачной сборке.
Можно ли деплоить без публичного порта?
Да — вместо приёмного webhook используй pull-модель: cron или GitHub Actions с self-hosted runner, который сам ходит за изменениями. Но входящий webhook быстрее реагирует на push.