[ ENGINEERING_GUIDE ][ CI_CD ][ GITHUB_ACTIONS ][ NEXTJS ][ DEVOPS ]

CI/CD for Next.js — GitHub Actions from tests to production (2026)

May 28, 202610 min read
Author: DevStudio.itWeb & AI Studio

Lint, build via scripts/build.cjs, Prisma, Vercel preview, and rollback — complete pipeline for Next.js 15 on Node 22, with an honest starting point (no workflows in repo yet).

READ_TIME: 10 MIN_COMPLEXITY: MED_
STAMP: VERIFIED_BY_DS_

TL;DR

For a Next.js project on Node 22, Next 15.5.18, and build via scripts/build.cjs, we recommend one GitHub Actions pipeline: every PR = quality (lint + build), merge to main = deploy to Vercel. This repository does not yet have a .github/workflows directory — this article describes the target state to add now instead of relying on manual "works on my machine." Keep secrets (VERCEL_TOKEN, DATABASE_URL, API keys) only in GitHub Secrets.

Who this is for

  • Teams of 1–5 developers on Next.js + Vercel
  • Projects with Prisma (postinstall: prisma generate, migrate in build script)
  • Companies wanting auditable deployment history and preview URL on every PR
  • Monorepos or multiple apps in one repo (paths: section)

Keywords (SEO)

ci cd nextjs github actions, nextjs pipeline 2026, vercel automatic deployment, github actions build nextjs, branch protection main, vercel rollback

Starting point: honest repo state

In many production projects the pipeline "exists in the team's heads," not in the repo. Here:

  • package.json requires Node 22.x (engines.node),
  • Next.js 15.5.18, scripts: buildnode scripts/build.cjs, linteslint, postinstallprisma generate,
  • No .github/workflows/*.yml files — CI must be added deliberately.

That is not a flaw — it is a normal stage. The flaw appears when laptop deploy skips the same steps as PR. The goal is one artifact: commit passed lint and build in Actions → same commit lands on Vercel.

What scripts/build.cjs does (and why CI must use it)

Plain next build in CI often is not enough when:

  1. Prisma — script loads .env / .env.local (not in repo in Actions), runs prisma generate, and with DATABASE_URL also prisma migrate deploy, then next build.
  2. Locally the developer has a database; CI build may pass without migrations if you do not set the secret — but generate is still needed after npm ci (also supported by postinstall).

Pipeline conclusion: build job must call npm run build (not raw next build), with environment variables in GitHub Secrets / Environments. For PRs without a database you may use shadow DB URL or skip migrations — per team policy; production must have full secrets in production environment.

DATABASE_URL strategies in CI (Prisma)

Strategy When Pros / cons
Same URL as staging Small team, one test DB Fast; risk of migration collision on parallel PRs
Separate DB per PR (Neon, Supabase branch) Larger team Isolation; higher cost / setup
No DATABASE_URL on PR Static front only Build may skip migrate deploy (log in build.cjs); schema must be current from repo
Full URL only on main Compromise PR: lint + tsc + build without migrate; prod: full pipeline

In a DevStudio-style build, without DATABASE_URL the script logs a message and skips prisma migrate deploy, but still runs prisma generate — aligned with postinstall and prevents "Client not generated" errors.

Step Pull request Push to main
npm ci yes yes
npm run lint (ESLint) yes yes
npx tsc --noEmit yes (if TypeScript in project) yes
npm run build yes yes
Unit tests yes when added yes
Vercel preview deploy yes (Git integration or Actions)
Vercel production deploy yes
Lighthouse CI optional optional (e.g. cron)

Rule: merge nothing to main that did not pass the same build as production.

Full workflow example (.github/workflows/ci.yml)

The file below is ready to copy and adapt. Uses Node 22, npm cache, lint, typecheck, and build matching this repo.

name: CI

on:
  pull_request:
    branches: [main]
  push:
    branches: [main]

concurrency:
  group: ci-${{ github.workflow }}-${{ github.ref }}
  cancel-in-progress: true

permissions:
  contents: read

jobs:
  quality:
    runs-on: ubuntu-latest
    timeout-minutes: 25
    steps:
      - name: Checkout
        uses: actions/checkout@v4

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

      - name: Install dependencies
        run: npm ci

      - name: ESLint
        run: npm run lint

      - name: TypeScript (no emit)
        run: npx tsc --noEmit

      - name: Build (Prisma + Next via scripts/build.cjs)
        run: npm run build
        env:
          # Required for full build — set in GitHub Secrets / Environment
          DATABASE_URL: ${{ secrets.DATABASE_URL }}
          NEXT_PUBLIC_SITE_URL: ${{ vars.NEXT_PUBLIC_SITE_URL || 'https://example.com' }}
          # Add remaining NEXT_PUBLIC_* and API keys as in Vercel Production

  deploy-preview:
    if: github.event_name == 'pull_request'
    needs: quality
    runs-on: ubuntu-latest
    environment: preview
    steps:
      - uses: actions/checkout@v4
      - uses: amondnet/vercel-action@v25
        with:
          vercel-token: ${{ secrets.VERCEL_TOKEN }}
          vercel-org-id: ${{ secrets.VERCEL_ORG_ID }}
          vercel-project-id: ${{ secrets.VERCEL_PROJECT_ID }}
          github-token: ${{ secrets.GITHUB_TOKEN }}
          scope: ${{ secrets.VERCEL_ORG_ID }}

  deploy-production:
    if: github.event_name == 'push' && github.ref == 'refs/heads/main'
    needs: quality
    runs-on: ubuntu-latest
    environment: production
    steps:
      - uses: actions/checkout@v4
      - uses: amondnet/vercel-action@v25
        with:
          vercel-token: ${{ secrets.VERCEL_TOKEN }}
          vercel-org-id: ${{ secrets.VERCEL_ORG_ID }}
          vercel-project-id: ${{ secrets.VERCEL_PROJECT_ID }}
          vercel-args: '--prod'
          github-token: ${{ secrets.GITHUB_TOKEN }}
          scope: ${{ secrets.VERCEL_ORG_ID }}

Simpler alternative: enable Vercel Git Integration — Vercel builds preview on PR, Actions quality job stays the merge gate. Many teams use both: Actions = lint/tsc/build, Vercel = hosting + URL.

Secrets and variables — where to put what

Name Where Notes
VERCEL_TOKEN GitHub Secret From Vercel Account Settings
VERCEL_ORG_ID, VERCEL_PROJECT_ID GitHub Secret From .vercel/project.json after vercel link
DATABASE_URL Secret + Vercel Env Same value as production for migrate deploy
OPENAI_API_KEY, Stripe, etc. Secret / Vercel Never in repo or Action logs
NEXT_PUBLIC_* Variables (non-secret) or Vercel Must be identical in PR build and prod or "works on my machine" returns

In repo settings: Settings → Secrets and variables → Actions. For production use Environment production with optional Required reviewers (1–2 people).

Branch protection on main

Minimum worth enabling from day one:

  • Require pull request before merging
  • Require status check quality (job name from workflow)
  • Require branch to be up to date
  • Do not allow bypassing (even admin, if the team has discipline)
  • Optional: require signed commits

Then nobody merges a build that did not pass npm run lint and npm run build on Node 22.

Preview URL on every PR

Vercel Git Integration (recommended to start):

  • automatic URL like project-git-branch-xyz.vercel.app,
  • bot comment on PR with link,
  • QA and client verify before merge.

GitHub Actions + vercel-action (as in yaml above):

  • full control, same token as prod deploy,
  • useful when you want one pipeline in YAML instead of Vercel panel.

In both cases ensure NEXT_PUBLIC_* on preview is not empty — otherwise PR build "passes" but preview UI differs from production.

Env sync: in Vercel copy variable set from Production to Preview (or vercel env pull). In GitHub add the same NEXT_PUBLIC_* as Variables (visible in logs, not secret) — then quality builds the same code as preview. Keep secrets (DATABASE_URL, STRIPE_SECRET_KEY) only in Secrets.

Monorepo: do not build everything on every PR

When one repo has e.g. apps/web and packages/ui, add separate workflows with path filters:

on:
  pull_request:
    paths:
      - 'apps/web/**'
      - 'packages/ui/**'
      - 'package-lock.json'

You can use paths-ignore for docs (docs/**, content/blog/**) to skip an 8-minute build for a typo in an article — unless the blog is part of the same Next.js package (in a marketing monolith you often still build everything).

Rollback on Vercel in ~60 seconds

Prod deploy on Vercel is a new deployment; previous builds remain.

Emergency procedure:

  1. Vercel Dashboard → Deployments
  2. Find last working deployment (previous commit)
  3. ⋯ → Promote to Production

Often under one minute. Pipeline does not replace rollback — it ensures the promoted build was once green on PR. Avoid laptop deploy "quick fix" beside CI — then panel rollback may restore a different version than you think.

On self-hosted (Docker): image tag = commit SHA; rollback = previous tag in orchestrator. Same rule: one artifact from CI.

Security in CI/CD

  • permissions: contents: read by default (as in example) — minimal GITHUB_TOKEN rights
  • Separate environment production with approval
  • Dependabot / npm audit in a weekly workflow
  • Never echo secrets or full .env in logs — Actions masks secrets.*, but custom scripts must stay clean
  • Rotate VERCEL_TOKEN and API keys quarterly or when a member leaves

Most common mistakes (and fixes)

Mistake Effect Fix
PR build without NEXT_PUBLIC_* Different bundle than prod Variables in GitHub + Vercel Preview
next build instead of npm run build Missing Prisma migrate/generate Always repo script
Node 20 in Actions, 22 in engines Random native module errors node-version: '22'
No npm cache 8 min instead of 3 cache: 'npm' in setup-node
Laptop deploy + CI Two sources of truth Only merge → main → deploy
No branch protection Broken prod Friday evening Required quality check

FAQ

Does GitHub Actions replace Vercel?

No. Actions = quality control and optional CLI deploy; Vercel = hosting, CDN, preview, rollback. Most often: Actions for lint/build, Vercel for running the app.

Must I run prisma migrate deploy in CI?

In this project scripts/build.cjs does it when DATABASE_URL is set. On PR you may use a test database or skip migrations — but ensure Prisma schema is consistent (generate is still required). On main/prod DATABASE_URL must be complete.

What if there are no tests?

Start with lint + tsc + build. Add tests when you have critical paths (payments, contract APIs). Pipeline still delivers ~80% of value.

Monorepo with three apps?

Three workflows with paths: or one workflow with strategy.matrix — not one global build without filters.

How to sync Node version with Vercel?

In Vercel Project Settings set Node.js 22.x, matching engines in package.json.

Troubleshooting: red build in Actions

  • prisma generate failed — check Node (22), whether npm ci finished, schema in repo valid.
  • migrate deploy failed — DB URL unreachable from GitHub (IP firewall), bad migration, conflict with manual DB changes.
  • next build OOM — larger runner (rare) or drop heavy analysis; check heavy library imports.
  • Lint fail on PR — run npm run lint locally on Node 22; do not merge with --no-verify.
  • Preview works, prod does not — compare Vercel Production vs Preview Environment Variables; often one missing NEXT_PUBLIC_*.

Action logs show the exact step — treat them as the only audit before touching production.

One-day rollout plan

  1. Add .github/workflows/ci.yml (fragment above).
  2. Configure Secrets + Variables (DATABASE_URL, Vercel, NEXT_PUBLIC_*).
  3. Enable branch protection on main.
  4. Connect repo to Vercel (if not yet).
  5. Open a test PR — verify green quality + preview URL.
  6. Merge — verify production deployment and save rollback procedure in team README.

Summary

CI/CD for Next.js 15 on Node 22 is not magic — it is a repeatable set: npm ci → lint → typecheck → npm run build (Prisma + Next) on every PR, deploy to Vercel after merge, secrets outside repo, preview for acceptance, rollback via Promote in the panel. No workflow in the repository is a signal to add it now — before a manual deploy costs more than one day of production repair.

Want a pipeline for your project?

  • Contact us — we review package.json, Vercel, and Prisma for your case
  • Web applications — Next.js, hosting, and DevOps in one package

About the author

We build fast websites, web/mobile apps, AI chatbots and hosting setups — with a focus on SEO and conversion.

Recommended links

From theory to production — Branchly, our hosting stack, care plans and shipped work.

LIKE HOW WE THINK? LET'S BUILD SOMETHING TOGETHER.

[ START_PROJECT_CONFIGURATION ]