A Next.js app on Vercel ships fast by default, but the default has no quality gate. This playbook walks the 10 steps to add one: a GitHub Actions workflow that typechecks and tests every PR, Vercel preview deploys per branch with branch-scoped env vars (so previews don't hit production data), branch protection on main, and a production deploy that runs only after CI is green. End state: nothing reaches production without passing through the gate.
Outcome
A Next.js app where every PR runs typecheck + lint + tests + a preview deploy automatically, main is branch-protected, and production only deploys after CI is green. Cost: $0 if you're under Vercel's hobby tier and GitHub Actions free minutes.
Time1-2 daysDifficultyintermediateForSolo founders + teams shipping a Next.js app on Vercel.
Top to bottom. Each step has tasks, pointers, gotchas.
01 / 10
Connect the GitHub repo to a Vercel project
15 min
Vercel auto-deploys every push to a GitHub branch as a preview, and pushes to your production branch as production. Connect the project once, set the production branch to main, and Vercel handles the build.
Tasks
Sign in to Vercel with GitHub
Click 'Add New' > Project > Import Git Repository
Pick the Next.js repo, accept the auto-detected framework preset
Set the production branch to main in Project Settings > Git
Vercel's monorepo support requires a Root Directory setting if your Next.js app is not at the repo root.
If you import a repo that already has a vercel.json, Vercel uses that instead of the auto-detected preset.
02 / 10
Set environment variables, branch-scoped
30 min
Vercel lets you set env vars at three scopes: Production, Preview, Development. The default is to apply a var to all three. That's fine for public values, dangerous for secrets and database URLs. Set DATABASE_URL separately for production vs preview, or your preview deploys will mutate prod data.
Tasks
List every env var your app reads (NEXT_PUBLIC_*, server-only, secrets)
For each, decide its scope: Production / Preview / Development
Set production secrets in Vercel > Project > Settings > Environment Variables, scope: Production only
Set preview env vars (e.g. branch-database URL) scoped Preview only
Verify with `vercel env pull .env.local` that the right values land
Vercel preview deploys hit your prod DB by default if DATABASE_URL is scoped 'All Environments'. Use a branch-database (Neon, Supabase, PlanetScale) and set DATABASE_URL per scope.
Env var changes don't auto-redeploy. Trigger a redeploy from the Deployments tab to pick them up.
NEXT_PUBLIC_* vars are baked at build time. Changing them requires a redeploy, not a runtime restart.
03 / 10
Use a branched database for preview deploys
1-2 hr
Branch-per-PR databases keep your preview deploys safely isolated from prod. Neon, Supabase, and PlanetScale all support branching: a PR opens, a fresh database branch spins up from a recent prod snapshot, the preview deploy hits that branch. PR closes, the branch goes away.
Tasks
Pick a Postgres-on-branches provider (Neon is cheapest for hobby use)
Create the production database; configure your Vercel Production env var to point at it
Wire up the integration so a PR creates a branch DB
In Vercel, set DATABASE_URL on Preview to the branch URL pattern from the integration
Open a test PR and verify the preview deploy hits the branch DB, not prod
Branch databases reset on PR close. If your migrations are not idempotent or you mutate seed data, your CI tests will pass on a clean branch and fail on a dirty prod.
Neon's free tier limits the number of compute-active branches at once. Hibernation is automatic but adds cold-start latency to the first preview hit.
04 / 10
Write the GitHub Actions CI workflow
2-3 hr
A CI workflow that typechecks, lints, and runs tests is the spine of the pipeline. Put it in `.github/workflows/ci.yml`. Run it on every PR. Use Node 20, cache npm or pnpm, and run jobs in parallel where possible.
Tasks
Create `.github/workflows/ci.yml` with jobs: typecheck, lint, test
Set the trigger to `pull_request` against main
Use `actions/checkout@v4` and `actions/setup-node@v4` with Node 20
Cache dependencies with the setup-node `cache` option
Run `next typecheck` (or `tsc --noEmit`), `next lint`, and your test runner
Push a PR, watch the workflow run, fix any plumbing issues
Default GITHUB_TOKEN permissions changed in 2023 to read-only. If a job writes back to the repo (PR comments, labels), set `permissions:` explicitly in the workflow.
Node version mismatch between local and CI is the #1 source of 'works on my machine' bugs. Pin Node 20 in both `.nvmrc` and the workflow.
Agent prompt for this step
Read the repo's package.json. Identify the scripts for typecheck, lint, and test (default to "tsc --noEmit", "next lint", and the test runner under "test").
Draft a GitHub Actions workflow at .github/workflows/ci.yml that:
1. Triggers on pull_request to main.
2. Runs three parallel jobs: typecheck, lint, test.
3. Uses Node 20 and the package manager already in lock file (npm / pnpm / yarn).
4. Caches node_modules.
5. Sets timeout-minutes: 10 per job to bound flakes.
Output the YAML inline. After the YAML, list any package.json script you'd add or rename to make the workflow cleaner.
05 / 10
Add Playwright end-to-end tests against preview deploys
Half a day to a day
Unit tests catch regressions in pure logic; E2E tests catch regressions in the actual user flow. Run Playwright against the live preview deploy URL on every PR. Vercel exposes the preview URL as a comment on the PR; GitHub Actions can pick it up via the `vercel-preview-url` action or by polling the deployment status.
Tasks
Install Playwright in the repo: `npm init playwright@latest`
Write 3-5 smoke tests covering the critical user path (signup, primary action, payment if applicable)
Add a workflow job that waits for the Vercel preview to deploy, then runs Playwright against that URL
Set Playwright `baseURL` to the preview URL so tests work locally and in CI
Vercel preview deploys take 30-180 seconds to build. Your Playwright job needs a poll-and-retry on the URL or you'll race and fail.
Playwright's default test timeout is 30 seconds. Slow preview deploys can blow that on the first request - bump it to 60 seconds or use `await page.waitForLoadState('networkidle')`.
Don't run Playwright against production. Always against preview. A flaky test mutating prod data is a paged-at-2-AM-Saturday situation.
06 / 10
Turn on branch protection for main
10 min
Branch protection on main makes the CI gate enforceable. Without it, a developer can merge a red PR. With it, GitHub blocks the merge until required checks pass. This is the cheapest, highest-leverage step in the playbook.
Tasks
Go to repo Settings > Branches > Add rule, branch pattern: main
Require a PR before merging
Require status checks to pass: typecheck, lint, test, e2e
Require branches to be up to date before merging
Optional: require 1 review approval (or a CODEOWNERS file for high-risk paths)
Save, then try to push direct to main to verify the block works
If you add a new required status check, GitHub doesn't retroactively block PRs that opened before the rule. Force a re-run on every open PR.
Admin bypass on branch protection is enabled by default. Turn it off (Include administrators checkbox) or admins forget the rule applies to them too.
07 / 10
Add Sentry for production error tracking
1-2 hr
Errors in production happen even with green CI. Sentry catches them, source-maps the stack to your deployed code, groups by fingerprint, and notifies you on regressions. Wire it in once, then never debug a production crash blind again.
Tasks
Sign up for Sentry, create a Next.js project
Install with `npx @sentry/wizard@latest -i nextjs`
Set `SENTRY_AUTH_TOKEN` as a Vercel env var (Production scope only) so source maps upload
Verify a thrown error in production reaches Sentry within 1 minute
Set up a Slack or email alert on new issue regressions
Sentry's source map upload silently no-ops if SENTRY_AUTH_TOKEN is missing. Errors land in the dashboard but with minified stack traces. Always check that `Releases` shows a recent entry.
Free Sentry tier is 5k errors/month. A noisy bot or a broken third-party script can blow through that in a day. Set rate limits in the project settings.
08 / 10
Configure deploy hooks and post-deploy smoke tests
1-2 hr
When production deploys, run a quick smoke test against the live URL. If it fails, page yourself. Vercel exposes a Deploy Hook URL you can hit to trigger redeploys, and a deploy webhook that fires on every successful prod deploy.
Tasks
Set up a Vercel deploy hook (Project > Settings > Git > Deploy Hooks) so you can trigger a deploy from CI
Add a GitHub Actions workflow that runs on `deployment_status: success` for production, runs a 5-test smoke suite against the live URL
If the smoke suite fails, send a Slack alert (Slack webhook URL stored as repo secret)
Document the rollback path: Vercel > Deployments > Promote a previous deployment
Vercel's instant rollback re-promotes a previous deploy without a rebuild. It's the right tool when production is on fire. Practice it before you need it.
Smoke tests against production should be read-only. A smoke test that creates a user and forgets to clean up will fill prod with junk in 30 days.
09 / 10
Add a staging environment for higher-risk changes
Half a day
For larger teams or risky changes, a staging branch + staging Vercel deployment gives you an extra integration layer between PRs and prod. Wire it up only if you need it: solo founders rarely do, teams of 5+ usually do.
Tasks
Create a `staging` branch in GitHub, mark it as a non-production branch in Vercel
Set up a Vercel custom domain (staging.your-domain.com) pointed at the staging branch
Configure env vars for the Preview scope to use staging credentials
Set up a workflow that auto-merges PRs from feature branches into staging on green CI
Set up a separate workflow that opens a `staging -> main` PR after staging passes a smoke test
Don't add staging if your team is under 5 engineers. The complexity isn't worth it; preview-per-PR catches the same class of bugs cheaper.
Staging databases drift from production schemas over time. Pin staging to weekly snapshots from prod or you'll catch bugs that only repro on stale data.
10 / 10
Document the pipeline and on-call runbook
2-3 hr
Six months from now, when CI breaks at 3 AM, the runbook is the difference between a 15-minute fix and a 3-hour fight. Document the workflow files, the env var scopes, the rollback path, and who owns each piece.
Tasks
Write a CI/CD section in the team's docs covering: workflow files, env var scopes, branch protection, rollback procedure
Document the smoke test failures and how to investigate (check Vercel logs, check Sentry, check the GitHub Actions run)
Add a CODEOWNERS file pointing high-risk paths (workflows, infra, payments) at a specific reviewer
Run a tabletop exercise: 'production is down, walk me through your first 5 minutes' - find gaps and fill them
Runbooks rot. Re-run the tabletop exercise quarterly and fix the gaps the team forgot about.
If the runbook's 'rollback procedure' has more than 5 steps, the rollback is too hard. Simplify Vercel's instant rollback first.
Agent prompt for this step
Draft a CI/CD runbook for this repo.
Read the workflow files in .github/workflows/, the package.json scripts, and the Vercel project config (ask the user for the env var list if you can't see it).
Output a Markdown doc with sections:
1. Pipeline overview (one-paragraph what-happens-when)
2. Workflow files reference (table: file / triggers / jobs)
3. Env var scopes (table: var / production / preview / development)
4. Rollback procedure (numbered steps)
5. Common failures (table: symptom / first place to check / common fix)
Output as the Brief surface so the team has a single linkable reference.
Hand the template to your agent
Workspace-wide agent prompt.
Paste this into your agent's permanent system prompt so the agent reads, writes, and maintains the template's surfaces as you work through the steps.
Agent system prompt
You are an agent on the "Set up CI/CD for Next.js" playbook workspace.
Your role: maintain the four surfaces (Steps, Pointers, Brief, CI runs) as the user wires up CI/CD.
Cadence:
- When the user marks a step Done, append a one-line summary to the Brief doc.
- When CI fails on a PR, capture the run as a CI runs row with the failing job + a one-line root cause.
- When the user adds a new env var or workflow file, mirror it into the Brief doc so the team has an up-to-date reference.
First MCP tool calls:
1. list_surfaces(workspace_slug="set-up-ci-cd-for-nextjs")
2. list_rows(workspace_slug="set-up-ci-cd-for-nextjs", surface_slug="steps")
3. get_doc(workspace_slug="set-up-ci-cd-for-nextjs", surface_slug="brief")
Read the user's repo first via the file system before drafting any workflow YAML, so the script names match.
FAQ
Common questions on this template.
What does this CI/CD setup actually cost?
If your team and traffic are small: $0. Vercel's Hobby tier is free up to 100GB bandwidth and unlimited preview deploys. GitHub Actions on private repos gets 2,000 free minutes per month, which is enough for ~200 PR runs at 10 min each. Sentry's Developer tier is free up to 5k errors. The first paid component you'll hit is usually Vercel Pro at $20/user/mo when you outgrow Hobby's bandwidth, or your branch-DB provider when you exceed their free branches.
Can I skip Playwright and just use unit tests?
If you're a solo founder shipping low-risk features, yes - unit tests + manual smoke catches 80%. The minute you have a paying customer flow (signup, payment, checkout), add at least 3-5 Playwright tests covering it. The cost of a broken signup is way higher than the cost of a flaky E2E.
Why not just use Vercel's built-in checks?
Vercel's automatic checks are limited to build success. Building successfully and being correct are different things: TypeScript errors are caught (because Next.js's build runs tsc), but lint, unit tests, and E2E aren't. The GitHub Actions workflow adds those layers; Vercel handles the deploy.
Should I add staging from day one?
No. Solo founders and teams under 5 engineers should run with preview-per-PR + main-is-production. Staging adds operational overhead (a second env to keep in sync, separate seed data, separate DNS) that's not worth it until you have an integration risk that previews can't catch. Add staging when a single PR routinely breaks because of interaction with another PR's changes.
Can my AI agents help maintain this pipeline?
Yes. Agents are useful for: drafting and updating workflow YAML when you add a new check, summarising flaky-test patterns from CI runs, drafting the runbook from the live config, and triaging which CI failures are real vs flakes. The playbook ships agent prompts inline for the workflow draft step and the runbook step.
Open this template as a workspace.
We mint a fresh copy in your org with the steps as table rows, the pointers as a separate table, and the brief as a doc. Bring your agents, start checking off boxes.