CI/CD Pipeline for Next.js: GitHub Actions to Vercel and Docker Deployments | SoniNow Blog

Limited TimeLearn More

ci/cdgithub actionsnext.jsdevopsdeployment

CI/CD Pipeline for Next.js: GitHub Actions to Vercel and Docker Deployments

Published

2026-06-23

Read Time

4 mins

CI/CD Pipeline for Next.js: GitHub Actions to Vercel and Docker Deployments

A well-designed CI/CD pipeline catches bugs before they reach production, provides preview environments for every pull request, and deploys changes with zero downtime. For Next.js applications, the pipeline needs to handle static generation, server-side rendered pages, API routes, and Docker-based self-hosting. Here is how to build it.

Continuous Integration: Testing and Linting

Every push to any branch should run your test suite, type checking, and linting. Keep the CI pipeline fast by using caching and only running jobs relevant to the changed code.

# .github/workflows/ci.yml
name: CI
on:
  push:
    branches: [main, develop]
  pull_request:
    branches: [main]

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

      - uses: pnpm/action-setup@v4
      - uses: actions/setup-node@v4
        with:
          node-version: 22
          cache: 'pnpm'

      - name: Install dependencies
        run: pnpm install --frozen-lockfile

      - name: TypeScript check
        run: pnpm typecheck

      - name: Lint
        run: pnpm lint

      - name: Unit tests
        run: pnpm test -- --coverage

      - name: Upload coverage
        uses: actions/upload-artifact@v4
        with:
          name: coverage
          path: coverage/

Add caching for Turborepo if you are in a monorepo. This reduces CI times from minutes to seconds for unchanged packages.

Preview Deployments for Every Pull Request

Vercel automatically creates preview deployments for every PR when your repo is connected. If you self-host or use Docker, create ephemeral preview environments using GitHub Actions.

deploy-preview:
  if: github.event_name == 'pull_request'
  needs: quality
  runs-on: ubuntu-latest
  environment:
    name: preview
    url: ${{ steps.deploy.outputs.url }}
  steps:
    - uses: actions/checkout@v4

    - name: Build Docker image
      run: |
        docker build \
          --build-arg NEXT_PUBLIC_API_URL=${{ secrets.STAGING_API_URL }} \
          -t app:${{ github.sha }} .

    - name: Deploy to preview cluster
      run: |
        kubectl set image deployment/app-preview \
          app=app:${{ github.sha }} \
          --namespace=preview
        kubectl rollout status deployment/app-preview \
          --namespace=preview --timeout=5m

    - name: Comment PR with URL
      uses: actions/github-script@v7
      with:
        script: |
          const url = `https://pr-${{ github.event.number }}.preview.example.com`
          github.rest.issues.createComment({
            issue_number: context.issue.number,
            owner: context.repo.owner,
            repo: context.repo.repo,
            body: `Preview deployed: ${url}`,
          })

Each preview gets its own URL. The PR author, reviewer, and QA team can test changes in isolation before merging.

Production Deployment Pipeline

Merging to the main branch triggers the production pipeline. This should include build, test, deploy, smoke test, and rollback capability.

# .github/workflows/deploy.yml
name: Deploy Production
on:
  push:
    branches: [main]

jobs:
  build:
    runs-on: ubuntu-latest
    outputs:
      image: ${{ steps.build.outputs.image }}
    steps:
      - uses: actions/checkout@v4

      - name: Build and push Docker image
        id: build
        run: |
          docker build \
            --cache-from ${{ secrets.REGISTRY }}/app:latest \
            -t ${{ secrets.REGISTRY }}/app:${{ github.sha }} \
            -t ${{ secrets.REGISTRY }}/app:latest .
          docker push ${{ secrets.REGISTRY }}/app:${{ github.sha }}
          docker push ${{ secrets.REGISTRY }}/app:latest
          echo "image=${{ secrets.REGISTRY }}/app:${{ github.sha }}" >> $GITHUB_OUTPUT

  deploy:
    needs: build
    runs-on: ubuntu-latest
    environment: production
    steps:
      - name: Deploy to production cluster
        run: |
          kubectl set image deployment/app \
            app=${{ needs.build.outputs.image }} \
            --namespace=production --record
          kubectl rollout status deployment/app \
            --namespace=production --timeout=5m

  smoke-test:
    needs: deploy
    runs-on: ubuntu-latest
    steps:
      - name: Run smoke tests
        run: |
          curl --fail --retry 10 --retry-delay 5 \
            https://example.com/api/health
          curl --fail --retry 5 --retry-delay 3 \
            https://example.com | grep -q '<html'

  rollback:
    if: failure()
    needs: [deploy, smoke-test]
    runs-on: ubuntu-latest
    steps:
      - name: Rollback to previous version
        run: |
          kubectl rollout undo deployment/app \
            --namespace=production

The rollback job only runs if either deployment or smoke tests fail. Automatic rollbacks prevent bad deployments from affecting users for more than a few minutes.

Dockerfile for Next.js Production

Your Docker image should be optimized for Next.js standalone output, which includes only the files needed to run the application.

FROM node:22-alpine AS builder
WORKDIR /app
COPY package.json pnpm-lock.yaml ./
RUN corepack enable && pnpm install --frozen-lockfile
COPY . .
RUN pnpm build

FROM node:22-alpine AS runner
WORKDIR /app
ENV NODE_ENV=production
COPY --from=builder /app/.next/standalone ./
COPY --from=builder /app/public ./public
COPY --from=builder /app/.next/static ./.next/static

EXPOSE 3000
CMD ["node", "server.js"]

Set output: 'standalone' in next.config.ts to create a self-contained build directory. This dramatically reduces the Docker image size and startup time.

Monitoring the Pipeline

Add Slack or Discord notifications for pipeline status. Track deployment frequency, change failure rate, and mean time to recovery (MTTR). A healthy pipeline should deploy multiple times a day with a change failure rate under 5%.

Building and maintaining CI/CD pipelines requires deep knowledge of both your application architecture and your deployment infrastructure. At SoniNow, we design deployment pipelines that automate testing, preview deployments, and production rollouts with built-in safety mechanisms and monitoring.

Need a CI/CD pipeline for your Next.js app? Contact SoniNow to discuss your deployment workflow and infrastructure requirements.