Code Agency
2 min read

Next.js + Turborepo + Woodpecker CI: fast builds, PR instances, no vendor lock-in

Remote turbo caching turns ten-minute builds into ten-second cache replays, and a self-hosted Woodpecker pipeline deploys every PR as its own preview instance. The setup behind this very website.

This website is a Next.js app in a Turborepo monorepo, built by a self-hosted Woodpecker CI, deployed to our own Kubernetes clusters — with a live preview instance for every pull request. No GitHub Actions minutes, no Vercel bill, no lock-in. Here's why this stack earns its keep.

Turborepo: never build the same thing twice

Turborepo hashes every task's inputs — source files, dependencies, environment — and caches the outputs. Unchanged input means the build is replayed, not repeated:

web:build: cache hit, replaying logs 62d4aba75ab14869
 Tasks:    2 successful, 2 total
  Time:    53ms >>> FULL TURBO

That's a full production build of this site in 53 milliseconds, because nothing changed since the last one.

Remote caching: the whole team shares one memory

The local cache is nice; the remote cache is the multiplier. Point turbo at a shared cache backend and every environment — your laptop, a colleague's machine, every CI runner — reads and writes the same artifact store:

turbo build --api="https://cache.internal" --team="codeagency" --token="$TURBO_TOKEN"

CI stops rebuilding what a developer already built locally. A pipeline that compiled everything in ten minutes now spends its time only on what actually changed. Self-hosted cache servers (like ducktors/turborepo-remote-cache) make this work without any Vercel account — it's an open protocol over HTTP.

Woodpecker: CI that lives in your cluster

Woodpecker is a lightweight, open-source CI that runs as a couple of pods in the same Kubernetes cluster as the workloads it deploys. Pipelines are a YAML file in the repo:

steps:
  install: { image: node:26-slim, commands: [corepack enable, pnpm install --frozen-lockfile] }
  quality: { commands: [pnpm lint, pnpm typecheck] }
  build:   { commands: [pnpm build] }

Because it's ours, there are no per-minute charges, no queue behind other people's builds, and no third party holding deploy credentials to our infrastructure. The runners are inside the cluster — deploying is a local operation.

PR preview instances: review running software, not diffs

The pipeline's best trick: every pull request gets labelled, built, and deployed as its own isolated instance with its own URL. Reviewers click through the actual feature on the actual stack — not a screenshot, not a local checkout. Merge it and the preview is torn down; the same tested artifact rolls to production.

Code review catches bad code. Preview instances catch bad software — the layout that breaks on a phone, the form that fails against real latency. Different bug classes, both caught before production.

The same recipe, any project

This exact setup — monorepo, shared cache, in-cluster CI, previews per PR — is what we bring to every custom web application we build and host. It's not exotic; it's just owning your pipeline instead of renting it.

Want us to publish something specific?

Tell us what you'd like to read and we'll add it to our writing queue.

Get the next one in your inbox

New articles, videos and the occasional engineering note — a short mail when there’s something worth reading, nothing else.