Деплой на VPS через GitHub Actions Перейти к содержимому
+7 (920) 556-26-91 пн–пт: 10:00–19:00 (МСК)
Проконсультироваться Обсудить задачу

Инструкции

CI/CD через GitHub Actions: автоматический деплой на VPS

Настройка workflow для деплоя по SSH, хранение секретов, сборка в CI или на сервере, безопасность ключа деплоя и порядок отката.

CI/CD через GitHub Actions: автоматический деплой на VPS

Короткий ответ: деплой через GitHub Actions — это workflow-файл в .github/workflows/, который запускается при пуше в ветку и по SSH выполняет на сервере команды обновления. Секреты (адрес, пользователь, ключ) хранятся в настройках репозитория и в файл не попадают. Сборку обычно делают в самом CI, а на сервер доставляют готовый результат — это экономит ресурсы сервера и сокращает время простоя.

Смежные темы: сервисы systemd, SSH-ключи и управление Node.js через PM2.

Что даёт автоматический деплой

Ручной деплой Автоматический
Забытые шаги: не выполнили сборку, не перезапустили сервис Последовательность команд одна и та же каждый раз
Нет записи, что именно и когда выкатывалось История запусков с результатом каждого
Правки прямо на сервере, расхождение с репозиторием Сервер — только результат, источник один
Откат — вручную и по памяти Откат повторным запуском предыдущей версии

Главная выгода не в скорости, а в предсказуемости: одинаковые команды выполняются одинаково, и человеческий фактор из процесса уходит.

Подготовка секретов

В репозитории: Settings → Secrets and variables → Actions. Нужны как минимум:

Секрет Значение
SSH_HOST Адрес сервера
SSH_USER Пользователь для деплоя
SSH_KEY Приватный ключ целиком, включая строки BEGIN и END
SSH_PORT Порт SSH, если он не 22

Для деплоя стоит завести отдельного пользователя, а не использовать root. Ему нужны права только на каталог приложения и на перезапуск конкретного сервиса — это делается через правило sudoers на одну команду.

Файл workflow

Создайте .github/workflows/deploy.yml:

name: Deploy

on:
  push:
    branches: [ main ]
  workflow_dispatch:

concurrency:
  group: deploy-production
  cancel-in-progress: false

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

      - name: Setup Node
        uses: actions/setup-node@v4
        with:
          node-version: '22'
          cache: 'npm'

      - name: Build
        run: |
          npm ci
          npm run build

      - name: Deploy over SSH
        uses: appleboy/ssh-action@v1
        with:
          host: ${{ secrets.SSH_HOST }}
          username: ${{ secrets.SSH_USER }}
          key: ${{ secrets.SSH_KEY }}
          port: ${{ secrets.SSH_PORT }}
          script: |
            set -e
            cd /var/www/app
            git fetch --all
            git reset --hard origin/main
            npm ci --omit=dev
            pm2 reload app
            echo "deployed $(git rev-parse --short HEAD)"

Разбор ключевых мест:

Фрагмент Зачем
concurrency Не даёт двум деплоям выполняться одновременно
workflow_dispatch Позволяет запустить деплой вручную из интерфейса
npm ci Устанавливает строго по lock-файлу, в отличие от npm install
set -e Останавливает выполнение при первой ошибке вместо продолжения
git reset --hard Приводит сервер к состоянию репозитория, убирая локальные правки
pm2 reload Перезапуск без разрыва соединений, в отличие от restart

Строка git reset --hard уничтожает изменения, сделанные прямо на сервере. Это и есть цель: источник истины — репозиторий. Если на сервере есть файлы, которых нет в git (например, конфигурация с секретами), их нужно вынести за пределы каталога деплоя.

Где собирать

Вариант Плюсы Минусы
Сборка в CI, доставка готового Сервер не нагружается, простой минимален Нужен способ доставки артефакта
Сборка на сервере Проще настроить Требует ресурсов, сервер занят во время сборки
Образ контейнера Окружение совпадает с локальным Требует реестра образов и работы с Docker

Для небольших проектов сборка на сервере приемлема. На слабом VPS сборка фронтенда может занять всю память — тогда её выносят в CI.

Безопасность

  • Ключ только для деплоя. Отдельная пара ключей, не та, которой вы заходите руками.
  • Ограничение по команде. В authorized_keys можно указать command= — тогда ключ позволит выполнить только разрешённый скрипт.
  • Секреты не в логах. GitHub маскирует значения секретов в выводе, но строка, попавшая в переменную окружения и напечатанная, может утечь.
  • Ветка защищена. Деплой по пушу в main безопасен только тогда, когда в main нельзя запушить напрямую.
  • Доступ к репозиторию. Тот, кто может запушить в ветку деплоя, может выполнить код на сервере.

Последний пункт часто недооценивают: автоматический деплой означает, что право записи в репозиторий равно праву выполнять команды на сервере.

Откат

# на сервере: вернуться на предыдущий коммит
cd /var/www/app
git log --oneline -10
git reset --hard <нужный-коммит>
npm ci --omit=dev
pm2 reload app

Для быстрого отката полезно, чтобы workflow принимал параметр с нужной ревизией. Тогда возврат к прежней версии выполняется кнопкой из интерфейса, а не через SSH.