Полное руководство по CI/CD для Next.js в GitLab: от базовой настройки до продакшн-пайплайна

30 марта 2026 г.

Настройка CI/CD для Next.js в GitLab — это не просто добавление файла .gitlab-ci.yml. Чтобы пайплайн работал быстро, надёжно и покрывал все сценарии (от разработки до продакшена), нужно учитывать особенности сборки Next.js, кэширования, типов деплоя и безопасности. В этом руководстве я детально разберу каждый этап, предоставлю готовые конфигурации с комментариями и объясню, как адаптировать их под ваши задачи.

Особенности Next.js в CI/CD: что нужно знать

Next.js имеет гибридную архитектуру, что накладывает особенности на сборку и деплой:

  • SSG (Static Site Generation) — страницы генерируются на этапе next build. В CI это означает, что все данные, необходимые для генерации, должны быть доступны в окружении сборки (переменные окружения, API).
  • SSR (Server-Side Rendering) — код выполняется на сервере. При деплое нужно убедиться, что Node.js окружение на целевом сервере соответствует требованиям.
  • ISR (Incremental Static Regeneration) — требует, чтобы сервер (или функция) могла перестраивать страницы. Это влияет на выбор платформы деплоя.
  • Image Optimization — может требовать установки дополнительных зависимостей (sharp) и наличия файловой системы для кэша.
  • Артефакты сборки — папка .next содержит как статику, так и серверный код. Важно правильно передавать её на этап деплоя.

Структура .gitlab-ci.yml: разбор stages и переменных

Файл .gitlab-ci.yml состоит из глобальных настроек, переменных, кэшей и списка джобов, сгруппированных по этапам (stages). Рассмотрим базовый каркас с комментариями:

# Определяем порядок выполнения этапов
stages:
  - install          # Установка зависимостей
  - test             # Линтинг и тесты
  - build            # Сборка приложения
  - deploy_staging   # Деплой на стейджинг
  - deploy_prod      # Деплой на продакшен

# Глобальные переменные, доступные во всех джобах
variables:
  NODE_VERSION: "20"                          # Версия Node.js
  NPM_CACHE: "$CI_PROJECT_DIR/.npm"           # Путь для кэша npm
  # Префикс для кэшей, чтобы различать ветки
  CACHE_KEY: "${CI_COMMIT_REF_SLUG}-${CI_JOB_NAME}"

# Кэширование на уровне проекта
cache:
  key: "${CI_COMMIT_REF_SLUG}"                # Ключ кэша зависит от ветки
  paths:
    - .npm/                                   # Кэш npm
    - node_modules/                           # Сами зависимости
  policy: pull-push                           # По умолчанию загружаем и сохраняем

# Дефолтные настройки для всех джобов (можно переопределить)
default:
  image: node:${NODE_VERSION}                 # Базовый образ для всех джобов
  before_script:
    - npm config set cache $NPM_CACHE         # Указываем npm путь для кэша
    - npm ci --cache $NPM_CACHE --prefer-offline || true  # Пытаемся восстановить кэш
  interruptible: true                         # Прерывать старые пайплайны при новом пуше

Этап 1: Установка зависимостей с кэшированием

Первый этап — установка зависимостей. Здесь мы используем npm ci для точного воспроизведения package-lock.json. Важно настроить кэш, чтобы ускорить последующие джобы.

install_dependencies:
  stage: install
  script:
    - npm ci --cache $NPM_CACHE --prefer-offline
  artifacts:                                  # Сохраняем node_modules как артефакт
    paths:
      - node_modules/
    expire_in: 1 hour                         # Храним недолго, чтобы не раздувать хранилище
  cache:
    key: "${CI_COMMIT_REF_SLUG}-node-modules"
    paths:
      - node_modules/
    policy: pull-push                         # Загружаем и сохраняем кэш

Почему npm ci вместо npm install? npm ci удаляет node_modules и устанавливает зависимости строго по lock-файлу, что гарантирует воспроизводимость сборки. Это идеально для CI.

Этап 2: Тестирование — линтер и unit-тесты

На этом этапе мы проверяем качество кода. Чтобы ускорить выполнение, используем артефакты от предыдущего этапа.

run_lint:
  stage: test
  script:
    - npm run lint
  dependencies:
    - install_dependencies                    # Используем node_modules из предыдущего этапа
  cache:                                      # Не сохраняем кэш для этого джоба
    policy: pull

run_tests:
  stage: test
  script:
    - npm run test -- --coverage              # Запускаем тесты с покрытием
  artifacts:
    paths:
      - coverage/                             # Сохраняем отчет о покрытии
    reports:
      coverage_report:
        coverage_format: cobertura
        path: coverage/cobertura-coverage.xml
    expire_in: 1 week
  dependencies:
    - install_dependencies
  cache:
    policy: pull

Если вы используете TypeScript, добавьте проверку типов отдельной командой: npm run type-check. Для ускорения можно запускать тесты параллельно, используя parallel: matrix в GitLab CI.

Этап 3: Сборка Next.js — SSG, SSR и артефакты

Сборка Next.js требует наличия всех переменных окружения, которые используются во время next build (например, для SSG или API-ключей). Также важно сохранить артефакты для деплоя.

build_nextjs:
  stage: build
  script:
    # Переменные окружения для сборки (можно передать через GitLab CI/CD variables)
    - export NEXT_PUBLIC_API_URL=$NEXT_PUBLIC_API_URL
    - export NEXT_PUBLIC_APP_ENV=$CI_ENVIRONMENT_NAME
    - npm run build
  artifacts:
    paths:
      - .next/                                # Основные артефакты сборки
      - public/                               # Публичные файлы
      - node_modules/                         # Нужны для запуска сервера
      - package.json
      - package-lock.json
    expire_in: 1 week
    reports:
      dotenv: build.env                       # Сохраняем переменные для следующих этапов
  dependencies:
    - install_dependencies
  cache:
    key: "${CI_COMMIT_REF_SLUG}-build"
    paths:
      - .next/cache/                          # Кэш Next.js для ускорения пересборок
    policy: pull-push
  after_script:
    # Сохраняем используемые переменные в файл для артефакта
    - echo "NEXT_PUBLIC_API_URL=$NEXT_PUBLIC_API_URL" > build.env
    - echo "NEXT_PUBLIC_APP_ENV=$CI_ENVIRONMENT_NAME" >> build.env

Важно: папка .next/cache/ значительно ускоряет последующие сборки. Добавьте её в кэш. Также обратите внимание, что для SSR-деплоя нужны node_modules, package.json и lock-файл.

Этап 4: Деплой — стратегии для разных сред

Выбор стратегии деплоя зависит от того, где будет работать ваше приложение. Рассмотрим три популярных варианта с полными примерами.

Деплой на Vercel через API-токен

Vercel — нативная платформа для Next.js. Для автоматизации используем Vercel CLI и токен.

deploy_vercel_staging:
  stage: deploy_staging
  image: node:${NODE_VERSION}
  script:
    - npm install -g vercel
    # Деплой на стейджинг с явным указанием окружения
    - vercel --token ${VERCEL_TOKEN} --scope ${VERCEL_TEAM} --prod --confirm --environment preview
  environment:
    name: staging/vercel
    url: https://${CI_COMMIT_REF_SLUG}.my-app.vercel.app
  only:
    - develop
    - merge_requests
  dependencies:
    - build_nextjs

# Продакшен деплой на Vercel
deploy_vercel_prod:
  stage: deploy_prod
  image: node:${NODE_VERSION}
  script:
    - npm install -g vercel
    - vercel --token ${VERCEL_TOKEN} --scope ${VERCEL_TEAM} --prod --confirm
  environment:
    name: production/vercel
    url: https://my-app.com
  only:
    - main
  dependencies:
    - build_nextjs
  when: manual                         # Ручной запуск для продакшена

Переменные окружения: VERCEL_TOKEN (токен аккаунта), VERCEL_TEAM (ID команды, если используется).

Деплой на VPS через SSH и rsync

Для деплоя на собственный сервер используем rsync для передачи файлов и перезапуска процесса через systemd или PM2.

deploy_vps:
  stage: deploy_prod
  image: alpine:latest
  before_script:
    - apk add --no-cache rsync openssh-client bash
    - mkdir -p ~/.ssh
    - echo "$SSH_PRIVATE_KEY" > ~/.ssh/id_rsa
    - chmod 600 ~/.ssh/id_rsa
    - eval $(ssh-agent -s)
    - ssh-add ~/.ssh/id_rsa
    - echo "$SSH_KNOWN_HOSTS" > ~/.ssh/known_hosts
  script:
    # Синхронизируем артефакты сборки
    - rsync -avz --delete --exclude '.git' --exclude '.env' .next/ $SSH_USER@$SSH_HOST:/var/www/my-app/.next/
    - rsync -avz --delete public/ $SSH_USER@$SSH_HOST:/var/www/my-app/public/
    - rsync -avz node_modules/ $SSH_USER@$SSH_HOST:/var/www/my-app/node_modules/
    - rsync -avz package.json package-lock.json $SSH_USER@$SSH_HOST:/var/www/my-app/
    # Запускаем скрипт обновления на сервере
    - ssh $SSH_USER@$SSH_HOST "cd /var/www/my-app && export NODE_ENV=production && pm2 reload ecosystem.config.js || pm2 start ecosystem.config.js"
  environment:
    name: production/vps
    url: https://my-app.com
  only:
    - main
  dependencies:
    - build_nextjs

Важно: на сервере должен быть установлен PM2 (npm install -g pm2) и создан файл ecosystem.config.js для управления процессом Next.js.

Деплой через Docker в GitLab Registry

Современный подход — упаковка приложения в Docker-образ. GitLab предоставляет встроенный Container Registry.

docker_build:
  stage: build
  image: docker:latest
  services:
    - docker:dind
  variables:
    DOCKER_TLS_CERTDIR: "/certs"
  before_script:
    - docker login -u $CI_REGISTRY_USER -p $CI_REGISTRY_PASSWORD $CI_REGISTRY
  script:
    - docker build --build-arg NEXT_PUBLIC_API_URL=$NEXT_PUBLIC_API_URL -t $CI_REGISTRY_IMAGE:${CI_COMMIT_SHORT_SHA} .
    - docker push $CI_REGISTRY_IMAGE:${CI_COMMIT_SHORT_SHA}
    - docker tag $CI_REGISTRY_IMAGE:${CI_COMMIT_SHORT_SHA} $CI_REGISTRY_IMAGE:latest
    - docker push $CI_REGISTRY_IMAGE:latest
  only:
    - main
    - develop

# Пример деплоя Docker-образа на сервер
deploy_docker:
  stage: deploy_prod
  image: alpine:latest
  before_script:
    - apk add --no-cache openssh-client
    - mkdir -p ~/.ssh
    - echo "$SSH_PRIVATE_KEY" > ~/.ssh/id_rsa
    - chmod 600 ~/.ssh/id_rsa
  script:
    - ssh $SSH_USER@$SSH_HOST "docker pull $CI_REGISTRY_IMAGE:${CI_COMMIT_SHORT_SHA} && docker stop my-app || true && docker rm my-app || true && docker run -d --name my-app -p 3000:3000 $CI_REGISTRY_IMAGE:${CI_COMMIT_SHORT_SHA}"
  only:
    - main
  dependencies: []

Для Docker-подхода в репозитории нужен Dockerfile с оптимизированной многоступенчатой сборкой Next.js.

Переменные окружения в GitLab: защита секретов

Все чувствительные данные (токены, ключи API, пароли) должны храниться в Protected Variables GitLab:

  1. Перейдите в Settings → CI/CD → Variables.
  2. Добавьте переменную, укажите ключ и значение.
  3. Включите Protected (доступна только для защищённых веток, например, main) и Masked (скрывает значение в логах).
  4. Для разных окружений можно использовать Environment scopes (например, production/*).

Пример использования в пайплайне: $VERCEL_TOKEN или $SSH_PRIVATE_KEY. Никогда не встраивайте секреты в код.

Оптимизация пайплайна: кэши, параллелизм, условные запуски

Чтобы пайплайн выполнялся быстро, используйте следующие приёмы:

  • Кэширование — кэшируйте node_modules и .next/cache. Ключ кэша должен меняться при изменении package-lock.json (используйте key: files: package-lock.json).
  • Параллельное выполнение — этапы test можно запускать параллельно, если они не зависят друг от друга.
  • Условные запуски — используйте only: changes, чтобы не запускать тяжёлые этапы при изменении документации:
only:
  changes:
    - "src/**/*"
    - "package.json"
    - "package-lock.json"
  • Limit resources — для self-hosted раннеров укажите тэги с нужными ресурсами.

Обработка ошибок и уведомления

При сбое пайплайна важно получать уведомления. GitLab позволяет настроить:

  • Slack-интеграцию через вебхук в настройках проекта.
  • Telegram-бота через кастомный скрипт в after_script.
  • Email-уведомления для членов команды.

Добавьте в критичные джобы (деплой) ручной подтверждение с помощью when: manual и allow_failure: false.

Полный пример .gitlab-ci.yml для production

Ниже представлен полный файл конфигурации, объединяющий все описанные элементы с комментариями:

stages:
  - install
  - test
  - build
  - deploy_staging
  - deploy_prod

variables:
  NODE_VERSION: "20"
  NPM_CACHE: "$CI_PROJECT_DIR/.npm"

cache:
  key: "${CI_COMMIT_REF_SLUG}"
  paths:
    - .npm/
    - node_modules/
  policy: pull-push

default:
  image: node:${NODE_VERSION}
  before_script:
    - npm config set cache $NPM_CACHE
    - npm ci --cache $NPM_CACHE --prefer-offline || true
  interruptible: true

install_dependencies:
  stage: install
  script:
    - npm ci --cache $NPM_CACHE --prefer-offline
  artifacts:
    paths:
      - node_modules/
    expire_in: 1 hour

run_lint:
  stage: test
  script:
    - npm run lint
  dependencies:
    - install_dependencies
  cache:
    policy: pull

run_tests:
  stage: test
  script:
    - npm run test -- --coverage
  artifacts:
    paths:
      - coverage/
    reports:
      coverage_report:
        coverage_format: cobertura
        path: coverage/cobertura-coverage.xml
  dependencies:
    - install_dependencies
  cache:
    policy: pull

build_nextjs:
  stage: build
  script:
    - export NEXT_PUBLIC_API_URL=$NEXT_PUBLIC_API_URL
    - npm run build
  artifacts:
    paths:
      - .next/
      - public/
      - node_modules/
      - package.json
      - package-lock.json
    expire_in: 1 week
  dependencies:
    - install_dependencies
  cache:
    key: "${CI_COMMIT_REF_SLUG}-build"
    paths:
      - .next/cache/
    policy: pull-push

deploy_vercel_staging:
  stage: deploy_staging
  image: node:${NODE_VERSION}
  script:
    - npm install -g vercel
    - vercel --token ${VERCEL_TOKEN} --scope ${VERCEL_TEAM} --prod --confirm --environment preview
  environment:
    name: staging/vercel
    url: https://${CI_COMMIT_REF_SLUG}.my-app.vercel.app
  only:
    - develop
    - merge_requests
  dependencies:
    - build_nextjs

deploy_vercel_prod:
  stage: deploy_prod
  image: node:${NODE_VERSION}
  script:
    - npm install -g vercel
    - vercel --token ${VERCEL_TOKEN} --scope ${VERCEL_TEAM} --prod --confirm
  environment:
    name: production/vercel
    url: https://my-app.com
  only:
    - main
  dependencies:
    - build_nextjs
  when: manual

Заключение

Мы детально разобрали настройку CI/CD для Next.js в GitLab — от базовых этапов до сложных стратегий деплоя с учётом особенностей фреймворка. Используя приведённые примеры и оптимизации, вы сможете создать надёжный и быстрый пайплайн, который автоматизирует выпуск вашего приложения. Не забывайте регулярно обновлять версии Node.js, проверять безопасность переменных и адаптировать конфигурацию под изменения в проекте.

Если вы ищете надёжную инфраструктуру для размещения вашего Next.js приложения, обратите внимание на облачные решения Timeweb Cloud или классический хостинг Timeweb, которые поддерживают Node.js и предоставляют удобные инструменты для CI/CD.