К основному содержимому

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 vs 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.

Статья помогла?
← Назад в базу знаний Задать вопрос поддержке