До основного вмісту

GitHub Actions: автоматичний деплой на VPS

Хмара та DevOps · 19.04.2026 · 4 хв читання
Ілюстрація до статті «GitHub Actions: автоматичний деплой на VPS»

GitHub Actions: CI/CD деплой на VPS

GitHub Actions дозволяє автоматично тестувати та розгортати застосунок на VPS при кожному push у репозиторій. Це основа сучасного DevOps без платних інструментів.

Підготовка VPS

# Створити користувача deploy
adduser deploy --disabled-password
usermod -aG www-data deploy
su - deploy
mkdir -p ~/.ssh && touch ~/.ssh/authorized_keys
chmod 700 ~/.ssh && chmod 600 ~/.ssh/authorized_keys

Налаштування GitHub Secrets

У репозиторії: Settings → Secrets and variables → Actions → New secret:

  • VPS_HOST — IP або домен сервера
  • VPS_USER — deploy
  • VPS_SSH_KEY — вміст ~/.ssh/id_rsa (приватний ключ)
  • VPS_PORT — 22 (або нестандартний)

Workflow для PHP проєкту

# .github/workflows/deploy.yml
name: Deploy to VPS

on:
  push:
    branches: [main]

jobs:
  deploy:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4

      - name: Setup PHP
        uses: shivammathur/setup-php@v2
        with:
          php-version: '8.3'

      - name: Install dependencies
        run: composer install --no-dev --optimize-autoloader

      - name: Run tests
        run: ./vendor/bin/phpunit

      - name: Deploy via SSH
        uses: appleboy/ssh-action@master
        with:
          host: ${{ secrets.VPS_HOST }}
          username: ${{ secrets.VPS_USER }}
          key: ${{ secrets.VPS_SSH_KEY }}
          port: ${{ secrets.VPS_PORT }}
          script: |
            cd /var/www/html
            git pull origin main
            composer install --no-dev --optimize-autoloader
            php artisan migrate --force
            php artisan config:cache
            php artisan route:cache
            php artisan view:cache
            sudo systemctl reload php8.3-fpm

Деплой через rsync

      - name: Deploy files via rsync
        uses: burnett01/rsync-deployments@7.0.1
        with:
          switches: -avzr --delete --exclude='.env' --exclude='storage/'
          path: ./
          remote_path: /var/www/html/
          remote_host: ${{ secrets.VPS_HOST }}
          remote_user: ${{ secrets.VPS_USER }}
          remote_key: ${{ secrets.VPS_SSH_KEY }}

Сповіщення в Telegram про деплой

      - name: Notify Telegram
        if: always()
        run: |
          STATUS=${{ job.status }}
          EMOJI=$([[ "$STATUS" == "success" ]] && echo "OK" || echo "FAIL")
          curl -s -X POST "https://api.telegram.org/bot${{ secrets.TG_TOKEN }}/sendMessage"             -d "chat_id=${{ secrets.TG_CHAT }}&text=${EMOJI} Deploy ${STATUS}: $GITHUB_REF_NAME"
Zero-downtime: Для PHP використовуйте rolling deploy: розгортайте в /releases/$(date), перемикайте симлінк і перезавантажуйте PHP-FPM. Це запобігає показу помилок під час деплою.

Розбір реальних помилок деплою

Дві помилки закривають майже всі проблеми з SSH-деплоєм через GitHub Actions.

Ключ не підходить для автентифікації

ssh: handshake failed: ssh: unable to authenticate, attempted methods [none publickey], no supported methods remain

Найчастіше причина в тому, що в секрет VPS_SSH_KEY скопійовано не весь приватний ключ — загубився перший або останній рядок (-----BEGIN OPENSSH PRIVATE KEY----- / -----END...-----), або ключ захищений паролем (passphrase), який екшен не може ввести інтерактивно. Згенеруйте окремий ключ без пароля спеціально для деплою: ssh-keygen -t ed25519 -N "" -f deploy_key.

Git не може оновити репозиторій на сервері

fatal: could not read Username for 'https://github.com': No such device or address

Репозиторій на сервері склоновано по HTTPS, а не по SSH, і для приватного репозиторію git запитує логін/пароль у неінтерактивному режимі — падає. Рішення: переклонувати через SSH із deploy-ключем сервера, доданим у GitHub як Deploy Key, або використати Personal Access Token у URL як частину секрету.

GitHub Actions проти GitLab CI

КритерійGitHub ActionsGitLab CI
Розташування конфігу.github/workflows/*.yml.gitlab-ci.yml у корені
Безкоштовні хвилини (публічні репо)НеобмеженоНеобмежено
СекретиSettings → Secrets and variablesSettings → CI/CD → Variables

Коли GitHub Actions не підходить

  • Деплой має йти в закритий контур без доступу в інтернет — GitHub-hosted runners не достукаються до внутрішньої мережі, потрібен self-hosted runner з окремим мережевим налаштуванням.
  • Репозиторій вже живе в self-hosted GitLab — логічніше використати вбудований GitLab CI, а не тягти другий CI-сервіс заради звички.
  • Секрети зобов'язані зберігатися в корпоративному Vault з аудитом кожного читання — GitHub Secrets це просто зашифроване сховище без детального логу доступу для compliance-звітності.
  • Потрібна збірка на специфічному залізі (ARM, GPU), якого немає в черзі GitHub-hosted runners — доведеться піднімати self-hosted runner на потрібному обладнанні в будь-якому разі.
  • У команді вже налагоджено Jenkins або TeamCity з усією історією пайплайнів — міграція лише заради модної назви не окупає ризик зламати робочий процес.

Перевірка результату

# Статус останнього workflow з терміналу
gh run list --limit 5
gh run view --log

# Переконатися, що на сервері справді задеплоєно потрібний коміт
ssh deploy@vps "cd /var/www/html && git rev-parse HEAD"

# Health-check після деплою
curl -sf https://yourdomain.com/health || echo "DEPLOY BROKEN"

Порівняння хешу коміту на сервері з git rev-parse HEAD у самому workflow — найнадійніший спосіб переконатися, що задеплоїлася саме та версія, яка пройшла тести, а не застарілий кеш. Для zero-downtime схеми з releases-директоріями і симлінком варто додатково перевіряти, що старий реліз ще доступний для відкату, а моніторинг самого сайту винести в Uptime Kuma, а не покладатися лише на статус workflow.

Чи була стаття корисною?
← Назад до бази знань Поставити питання підтримці