Настройка GitLab CI-CD через SSH на VPS
1. Предварительная информация
У кого есть опыт в CI/CD, могут сразу смотреть разделы Настройка деплоя (Deploy Stage) или Настройка тестирования (Test Stage).
Краткая предистория этих заметок
Впервые я столкнулся с методологией CI/CD в 2021 году, когда перешёл из аутсорса в продуктовую компанию и стал активно релизить проекты в команде. В то время разработчикам не особо приходилось погружаться в эту тему — это была компетенция DevOps. Но когда я решил завести собственный блог, вопрос автоматизации деплоя стал для меня важным. В итоге появились заметки, которыми поделюсь в этом посте.
Почему я выбрал CI/CD через SSH на VPS?
У меня обычный блог на PHP/Laravel, и ключевым требованием стала простота реализации. Я рассматривал также вариант деплоя через Runner на VPS, но в силу ограниченных ресурсов VPS и отсутствия необходимости сборки и тестирования проекта в едином окружении решил использовать SSH-подход.
Что уже должно быть настроено
Предполагается, что уже настроены следующие моменты:
- поднят VPS с Ubuntu;
- настроен доступ к VPS по SSH-ключам;
- код проекта залит в репозиторий GitLab.
Версия GitLab >= 16.11
2. Настройка деплоя (Deploy Stage)
В данном разделе описан простой пример деплоя проекта из репозитория GitLab на VPS. Деплой срабатывает при любом варианте обновления ветки master:
- прямой push изменений в ветку;
- после слияния MR (например,
Merge branch 'develop' into 'master'); - ручной запуск Pipeline.
2.1. Алгоритм действий
- Подготовка bash скрипта для деплоя
- Настройка переменных в GitLab
- Настройка файла .gitlab-ci.yml (Часть Deploy)
2.2. Подготовка bash скрипта для деплоя
Я использую bash-скрипт ~/deploy.sh для автоматизации команд деплоя на VPS. Пример базовых команд из моего скрипта:
#!/bin/bash project_dir="/var/www/laravel" ssh_key="~/.ssh/gitlab_rsa" eval `ssh-agent -s` ssh-add "$ssh_key" cd "$project_dir" git pull origin master php artisan migrate --force php artisan optimize
Реальный скрипт сложнее: с бэкапом БД и сопутствующими процессами.
Важно выдать права на выполнение:
chmod +x ~/deploy.sh
2.3. Настройка переменных в GitLab
Эти переменные будут использоваться в Pipeline для организации процесса деплоя.
Переходим: Project → Settings - CI/CD → Variables
Рекомендуемый набор:
SSH_KEY— приватный ключ для доступа к серверу. Лучше задать тип File.SSH_USER— имя пользователя на VPS.SSH_HOST— IP-адрес или домен сервера.

На скриншоте я не использовал тип Environments, поскольку у меня только production-окружение. Но при наличии других окружений (stage, feature...) эта настройка может понадобиться.
Если SSH_KEY имеет тип File, GitLab создаёт временный файл и подставляет в переменную путь к нему. Тогда в before_script можно использовать:
cp "$SSH_KEY" ~/.ssh/id_rsa
Если вы храните ключ как обычную переменную типа Variable, нужно записывать его в файл вручную:
printf '%s\n' "$SSH_KEY" > ~/.ssh/id_rsa
2.4. Настройка файла .gitlab-ci.yml (Часть Deploy)
В корне проекта создаём файл .gitlab-ci.yml со следующими настройками:
stages:
- deploy
deploy:
image: alpine:latest
stage: deploy
only:
- master
before_script:
- apk add --no-cache openssh-client
- mkdir -p ~/.ssh
- chmod 700 ~/.ssh
- cp "$SSH_KEY" ~/.ssh/id_rsa
- chmod 600 ~/.ssh/id_rsa
- ssh-keyscan $SSH_HOST > ~/.ssh/known_hosts 2>/dev/null
script:
- ssh $SSH_USER@$SSH_HOST "bash ~/deploy.sh"
После добавления изменений в ветку master появится Pipeline для Stage: deploy. Его проще всего увидеть в разделе: Project → Build → Pipelines.

3. Настройка тестирования (Test Stage)
При необходимости запуска тестов и других команд для проверки работоспособности кода можно добавить соответствующий этап. Опишу пример запуска тестов на этапе создания Merge Request из ветки develop в master.
3.1. Алгоритм действий
- Подготовка проекта к тестам
- Настройка файла .gitlab-ci.yml (Часть Test)
3.2. Подготовка проекта к тестам
Я использую PHPUnit для тестов и соответственно часть кода покрыл unit и интеграционными тестами. В проекте использую отдельный файл .env.testing:
APP_NAME=Laravel APP_ENV=testing LOG_CHANNEL=nullable DB_CONNECTION=sqlite DB_DATABASE=:memory: CACHE_DRIVER=array SESSION_DRIVER=array QUEUE_CONNECTION=sync BCRYPT_ROUNDS=4 MAIL_DRIVER=array
Запуск тестов делаю через команду:
composer test
Команда реализована через нотацию scripts в composer.json:
{
// ...
"scripts": {
// ...
"test": "APP_ENV=testing php -d memory_limit=512M vendor/bin/phpunit"
}
}
Нам нужно запустить тесты в виртуальном окружении, но перед этим его нужно собрать.
3.3. Настройка файла .gitlab-ci.yml (Часть Test)
Продолжаем редактировать .gitlab-ci.yml и добавляем новый этап для тестирования:
stages:
- test
test:
stage: test
image: php:8.4-fpm
rules:
- if: '$CI_PIPELINE_SOURCE == "merge_request_event" && ($CI_MERGE_REQUEST_TARGET_BRANCH_NAME == "master" || $CI_MERGE_REQUEST_TARGET_BRANCH_NAME == "develop")'
when: always
- when: never
before_script:
- apt-get update && apt-get install -y --no-install-recommends libzip-dev zip unzip libxml2-dev sqlite3 libsqlite3-dev
- docker-php-ext-install zip bcmath xml pdo pdo_sqlite
- curl -sS https://getcomposer.org/installer | php -- --install-dir=/usr/local/bin --filename=composer
- cp .env.testing .env
- composer install --no-progress --no-suggest
- php artisan config:clear
script:
- composer test
Сейчас я использую SQLite, но изначално в проекте был MySQL. Ниже пример для MySQL:
services: - mysql:8.0 variables: MYSQL_ROOT_PASSWORD: "root" MYSQL_DATABASE: "laravel_test"
После создания Merge Request из ветки develop в master появится новый Pipeline для Stage: test. Как и в случае с деплоем, его можно проверить в разделе: Project → Build → Pipelines.

4. Дополнение
4.1. Немного о Pipelines и Jobs
С первого раза вряд ли получится всё запустить правильно. Рекомендую не торопиться и создать тестовую ветку, на примере которой можно потренироваться с запуском и отладкой деплоя или тестов.
DevOps обычно так и делают, чтобы не портить основную ветку тестовыми коммитами.
Основные разделы, с которыми стоит познакомиться:
- Project → Build → Pipelines
- Project → Build → Jobs
4.2. Полный пример файла .gitlab-ci.yml
Ниже представлен полный пример файла .gitlab-ci.yml с этапами тестирования и деплоя.
stages:
- test
- deploy
test:
stage: test
image: php:8.4-fpm
rules:
- if: '$CI_PIPELINE_SOURCE == "merge_request_event" && ($CI_MERGE_REQUEST_TARGET_BRANCH_NAME == "master" || $CI_MERGE_REQUEST_TARGET_BRANCH_NAME == "develop")'
when: always
- when: never
before_script:
- apt-get update && apt-get install -y --no-install-recommends libzip-dev zip unzip libxml2-dev sqlite3 libsqlite3-dev
- docker-php-ext-install zip bcmath xml pdo pdo_sqlite
- curl -sS https://getcomposer.org/installer | php -- --install-dir=/usr/local/bin --filename=composer
- cp .env.testing .env
- composer install --no-progress --no-suggest
- php artisan config:clear
script:
- composer test
deploy:
image: alpine:latest
stage: deploy
only:
- master
before_script:
- apk add --no-cache openssh-client
- mkdir -p ~/.ssh
- chmod 700 ~/.ssh
- cp "$SSH_KEY" ~/.ssh/id_rsa
- chmod 600 ~/.ssh/id_rsa
- ssh-keyscan $SSH_HOST > ~/.ssh/known_hosts 2>/dev/null
script:
- ssh $SSH_USER@$SSH_HOST "bash ~/deploy.sh"
4.3. Что дальше?
Это всего лишь базовая часть CI/CD, которая позволяет автоматизировать релиз проекта. Дальше можно углубляться в такие темы, как настройка собственного registry, кеширование, артефакты и другие особенности настройки и оптимизации CI/CD.
5. Источники и ссылки
- GitLab CI/CD variables — GitLab Docs
- CI/CD YAML syntax reference — GitLab Docs
- GitLab CI/CD examples — GitLab Docs
- Using SSH keys with GitLab CI/CD — GitLab Docs