Kurzfassung
Für ein Next.js-Projekt auf Node 22, Next 15.5.18 und Build via scripts/build.cjs empfehlen wir eine GitHub-Actions-Pipeline: jeder PR = Qualität (lint + build), Merge auf main = Deploy auf Vercel. In diesem Repository gibt es noch keinen Ordner .github/workflows — der Artikel beschreibt den Zielzustand, den Sie gleich anlegen sollten, statt auf manuelles „bei mir geht’s“ zu vertrauen. Secrets (VERCEL_TOKEN, DATABASE_URL, API-Keys) nur in GitHub Secrets.
Für wen ist das
- Teams mit 1–5 Entwicklern auf Next.js + Vercel
- Projekte mit Prisma (
postinstall:prisma generate, Migrate im Build-Skript) - Firmen, die auditierbare Deploy-Historie und Preview-URL pro PR wollen
- Monorepos oder mehrere Apps in einem Repo (Abschnitt
paths:)
Keyword (SEO)
ci cd nextjs github actions, pipeline nextjs 2026, vercel deployment automatisieren, github actions build nextjs, branch protection main, vercel rollback
Ausgangspunkt: ehrlicher Repo-Status
In vielen Produktionsprojekten „existiert“ die Pipeline im Kopf des Teams, nicht im Repo. Hier:
package.jsonverlangt Node 22.x (engines.node),- Next.js 15.5.18, Skripte:
build→node scripts/build.cjs,lint→eslint,postinstall→prisma generate, - Keine Dateien
.github/workflows/*.yml— CI muss bewusst ergänzt werden.
Das ist kein Mangel — normaler Reifegrad. Problematisch wird es, wenn Deploy vom Laptop dieselben Schritte wie der PR umgeht. Ziel: ein Artefakt — Commit bestand lint und build in Actions → derselbe Commit landet auf Vercel.
Was scripts/build.cjs macht (und warum CI es braucht)
Rohes next build in CI reicht oft nicht, wenn:
- Prisma — Skript lädt
.env/.env.local(in Actions nicht im Repo), führtprisma generateaus und beiDATABASE_URLauchprisma migrate deploy, am Endenext build. - Lokal hat der Entwickler eine DB; in CI kann der Build ohne Migration durchlaufen, wenn kein Secret gesetzt ist — aber generate ist nach
npm cinötig (auch viapostinstall).
Pipeline-Folgerung: Build-Job muss npm run build (nicht rohes next build) mit passenden Env-Variablen in GitHub Secrets / Environments aufrufen. Für PR ohne DB reicht oft DATABASE_URL auf Shadow-DB oder Migration weglassen — je nach Team-Policy; Prod braucht volle Secrets im Environment production.
DATABASE_URL-Strategien in CI (Prisma)
| Strategie | Wann | Plus / Minus |
|---|---|---|
| Gleiche URL wie Staging | Kleines Team, eine Test-DB | Schnell; Migrations-Kollision bei parallelen PRs |
| Eigene DB pro PR (Neon, Supabase Branch) | Größeres Team | Isolation; mehr Kosten / Setup |
Kein DATABASE_URL im PR |
Nur statisches Frontend | Build kann migrate deploy überspringen (Log in build.cjs); Schema muss im Repo aktuell sein |
Volle URL nur auf main |
Kompromiss | PR: lint + tsc + build ohne Migrate; Prod: volle Pipeline |
Im DevStudio-Style-Build ohne DATABASE_URL schreibt das Skript eine Meldung und überspringt prisma migrate deploy, führt aber prisma generate aus — konsistent mit postinstall, verhindert „Client not generated“.
Empfohlene Aufteilung: PR vs. main
| Schritt | Pull Request | Push auf main |
|---|---|---|
npm ci |
ja | ja |
npm run lint (ESLint) |
ja | ja |
npx tsc --noEmit |
ja (bei TypeScript) | ja |
npm run build |
ja | ja |
| Unit-Tests | ja, wenn vorhanden | ja |
| Vercel-Deploy Preview | ja (Git-Integration oder Actions) | — |
| Vercel-Deploy Production | — | ja |
| Lighthouse CI | optional | optional (z. B. Cron) |
Regel: nichts in main mergen, was nicht denselben build wie Produktion bestanden hat.
Vollständiges Workflow-Beispiel (.github/workflows/ci.yml)
Die folgende Datei ist kopierfertig anpassbar. Nutzt Node 22, npm-Cache, lint, typecheck und build wie in diesem 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:
DATABASE_URL: ${{ secrets.DATABASE_URL }}
NEXT_PUBLIC_SITE_URL: ${{ vars.NEXT_PUBLIC_SITE_URL || 'https://example.com' }}
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 }}Einfachere Alternative: Vercel Git Integration aktivieren — Vercel baut Preview pro PR, Job quality in Actions bleibt Merge-Gate. Viele Teams kombinieren: Actions = lint/tsc/build, Vercel = Hosting + URL.
Secrets und Variablen — wo was liegt
| Name | Wo | Hinweise |
|---|---|---|
VERCEL_TOKEN |
GitHub Secret | Token aus Vercel Account Settings |
VERCEL_ORG_ID, VERCEL_PROJECT_ID |
GitHub Secret | Aus .vercel/project.json nach vercel link |
DATABASE_URL |
Secret + Vercel Env | Gleicher Wert wie Prod für migrate deploy |
OPENAI_API_KEY, Stripe usw. |
Secret / Vercel | Nie im Repo oder in Actions-Logs |
NEXT_PUBLIC_* |
Variables (nicht geheim) oder Vercel | Identisch in PR-Build und Prod, sonst kehrt „bei mir geht’s“ zurück |
Im Repo: Settings → Secrets and variables → Actions. Für Produktion Environment production mit optional Required reviewers (1–2 Personen).
Branch Protection auf main
Minimum ab Tag eins:
- Require pull request before merging
- Require status check quality (Job-Name aus Workflow)
- Require branch to be up to date
- Do not allow bypassing (auch Admin, wenn das Team diszipliniert ist)
- Optional: require signed commits
So landet kein Build in Prod, der npm run lint und npm run build auf Node 22 nicht bestanden hat.
Preview-URL pro PR
Vercel Git Integration (empfohlen zum Start):
- automatische URL wie
projekt-git-branch-xyz.vercel.app, - Bot-Kommentar im PR mit Link,
- QA und Kunde prüfen vor Merge.
GitHub Actions + vercel-action (wie im YAML oben):
- volle Kontrolle, derselbe Token wie Prod-Deploy,
- sinnvoll bei einem Pipeline-YAML statt Vercel-Panel.
In beiden Fällen: NEXT_PUBLIC_* auf Preview nicht leer — sonst grüner PR-Build, aber anderes UI auf Preview als Prod.
Env-Sync: in Vercel Variablen von Production nach Preview kopieren (oder Vercel CLI vercel env pull). In GitHub dieselben NEXT_PUBLIC_* als Variables — dann baut Job quality denselben Code wie Preview. Secrets (DATABASE_URL, STRIPE_SECRET_KEY) nur in Secrets.
Monorepo: nicht alles bei jedem PR bauen
Bei apps/web und packages/ui im selben Repo separate Workflows mit Pfadfilter:
on:
pull_request:
paths:
- 'apps/web/**'
- 'packages/ui/**'
- 'package-lock.json'Oder paths-ignore für Doku (docs/**, content/blog/**), um keinen 8-Minuten-Build bei Blog-Tippfehler zu starten — sofern der Blog nicht Teil desselben Next.js-Pakets ist (bei monolithischer Marketing-Site oft trotzdem Full-Build).
Rollback auf Vercel in ~60 Sekunden
Prod-Deploy auf Vercel ist ein neues Deployment; alte Builds bleiben.
Notfallprozedur:
- Vercel Dashboard → Deployments
- Letztes funktionierendes Deployment finden (vorheriger Commit)
- ⋯ → Promote to Production
Oft unter einer Minute. Pipeline ersetzt Rollback nicht — sie stellt sicher, dass der promotierte Build einmal grün im PR war. Deploy vom Laptop „schnell“ neben CI vermeiden — sonst kann Rollback im Panel eine andere Version wiederherstellen als erwartet.
Bei eigenem Server (Docker): Image-Tag = Commit-SHA; Rollback = vorheriger Tag im Orchestrator. Gleiche Regel: ein Artefakt aus CI.
Sicherheit in CI/CD
permissions: contents: readstandardmäßig — minimaleGITHUB_TOKEN-Rechte- separates Environment
productionmit Approval - Dependabot /
npm auditin wöchentlichem Workflow - Secrets und volle
.envnie in Logsechoen — Actions maskiertsecrets.*, eigene Skripte müssen sauber sein - Rotation von
VERCEL_TOKENund API-Keys quartalsweise oder nach Team-Austritt
Häufige Fehler (und Vermeidung)
| Fehler | Folge | Fix |
|---|---|---|
PR-Build ohne NEXT_PUBLIC_* |
Anderes Bundle als Prod | Variables in GitHub + Vercel Preview |
next build statt npm run build |
Kein Prisma migrate/generate | Immer Repo-Skript |
Node 20 in Actions, 22 in engines |
Zufällige native Modul-Fehler | node-version: '22' |
| Kein npm-Cache | 8 Min statt 3 | cache: 'npm' in setup-node |
| Deploy vom Laptop + CI | Zwei Wahrheiten | Nur Merge → main → Deploy |
| Keine Branch Protection | Prod-Bruch am Freitagabend | Pflicht-Check quality |
FAQ
Ersetzen GitHub Actions Vercel?
Nein. Actions = Qualitätskontrolle und optional CLI-Deploy; Vercel = Hosting, CDN, Preview, Rollback. Meist: Actions für lint/build, Vercel für App-Ausführung.
prisma migrate deploy in CI?
In diesem Projekt macht das scripts/build.cjs, wenn DATABASE_URL gesetzt ist. Im PR Test-DB oder Migration weglassen — Schema muss trotzdem passen (generate nötig). Auf main/prod muss DATABASE_URL vollständig sein.
Was ohne Tests?
Mit lint + tsc + build starten. Tests hinzufügen bei kritischen Pfaden (Zahlung, Vertrags-API). Pipeline liefert schon ~80 % Wert.
Monorepo mit drei Apps?
Drei Workflows mit paths: oder ein Workflow mit strategy.matrix — kein globaler Build ohne Filter.
Node-Version mit Vercel synchronisieren?
In Vercel Project Settings Node.js 22.x, passend zu engines in package.json.
Troubleshooting: roter Build in Actions
prisma generatefailed — Node (22), obnpm cifertig, Schema im Repo korrekt.migrate deployfailed — DB von GitHub aus unreachable (Firewall), falsche Migration, Konflikt mit manuellen DB-Änderungen.next buildOOM — Runner upgraden (selten) oder schwere Imports prüfen.- Lint fail im PR — lokal
npm run lintauf Node 22; nicht mit--no-verifymergen. - Preview OK, Prod nicht — Vercel Production vs. Preview Environment Variables vergleichen; oft fehlt ein
NEXT_PUBLIC_*.
Actions-Logs sind der Audit vor Produktion — genau einen Schritt lesen, der rot ist.
Cache und parallele PRs
Mit concurrency: cancel-in-progress: true bricht ein neuer Push auf demselben Branch den laufenden CI-Lauf ab — spart Minuten bei schnellen Fix-Commits. Ergänzen Sie npm- und .next/cache-Caching in Actions, wenn Builds regelmäßig über sechs Minuten dauern. Bei Prisma-Projekten: prisma generate läuft ohnehin über postinstall — teure Schritte sind meist next build und Typecheck; dort lohnt sich stabile Node-22-Version und identische Lockfile-Disziplin (npm ci, nie npm install in CI).
Plan — Implementierung an einem Tag
.github/workflows/ci.ymlhinzufügen (Fragment oben).- Secrets + Variables (DATABASE_URL, Vercel, NEXT_PUBLIC_*).
- Branch Protection auf
main. - Repo mit Vercel verbinden (falls noch nicht).
- Test-PR — grüner
quality+ Preview-URL. - Mergen — Production-Deployment prüfen, Rollback-Prozedur ins Team-README.
Zusammenfassung
CI/CD für Next.js 15 auf Node 22 ist kein Hexenwerk — wiederholbar: npm ci → lint → typecheck → npm run build (Prisma + Next) pro PR, Deploy auf Vercel nach Merge, Secrets außerhalb des Repos, Preview zur Abnahme, Rollback via Promote im Panel. Fehlende Workflows im Repository sind das Signal, das jetzt zu ergänzen — bevor manueller Deploy mehr kostet als ein Tag Produktions-Reparatur.
Pipeline für Ihr Projekt?
- Kontakt aufnehmen —
package.json, Vercel und Prisma für Ihren Fall prüfen - Web-Apps — Next.js, Hosting und DevOps aus einer Hand