Back to blog
DEVOPS 9 min read Dec 28, 2025

Mastering Monorepo CI/CD Pipelines

Best practices for building, testing, and deploying microservices from a single repository using Turborepo and GitHub Actions.

Stefan Nikolić

Senior SRE

Monorepos promise simplified dependency management, atomic cross-service changes, and a single source of truth. But without proper CI/CD tooling, they deliver something else entirely: 45-minute build times, flaky tests from unrelated services, and deploy-everything-on-every-commit pipelines that bring your entire release process to a halt.

We migrated our 23-service platform from separate repositories to a Turborepo-powered monorepo. Here's what we learned about building CI/CD pipelines that actually scale.

The Fundamental Problem: Affected Analysis

The key to monorepo CI/CD is answering one question: what changed? If a developer modifies the payment service, there's no reason to build, test, and deploy the notification service. Your pipeline needs to determine the blast radius of every change and only execute tasks for affected packages.

Turborepo handles this through its dependency graph. When you run `turbo run build --filter=...[HEAD~1]`, it identifies which packages have changed since the last commit, which packages depend on those changed packages (transitively), and only runs the build task for affected packages.

Pipeline Architecture

We structure our GitHub Actions pipeline into three stages. Stage one runs on every push: lint, type-check, and unit tests for affected packages only. This completes in under 3 minutes for typical changes.

Stage two runs on pull requests: integration tests, preview deployments, and security scanning. Integration tests run against shared infrastructure (database, message queue) but only for affected services. Preview deployments spin up isolated environments for each PR using Kubernetes namespaces.

Stage three runs on merge to main: production builds, container image creation, and staged rollouts. We use a canary deployment strategy where 5% of traffic goes to the new version, and automated health checks promote or roll back after 10 minutes.

Caching Strategy

Turborepo's remote cache is the single biggest performance win. Build artifacts, test results, and lint outputs are cached by content hash and shared across all developers and CI runners. When a package hasn't changed, its build completes in milliseconds because Turborepo just downloads the cached output.

We host our own remote cache on S3 behind a CloudFront distribution. Cache hit rates average 78%, which means most CI runs only build the packages that actually changed. Our median CI time dropped from 23 minutes to 4 minutes after enabling remote caching.

Dependency Management

The trickiest part of monorepo CI/CD is handling shared dependencies. When a shared library changes, every package that imports it needs to be rebuilt and retested. We mitigate this by keeping shared libraries small and focused, using semantic versioning within the monorepo (yes, internal packages have versions too), and running the full test suite nightly regardless of changes.

The Human Side

Tooling only solves half the problem. The other half is process. We established clear ownership for every package, required two-reviewer approval for changes to shared libraries, and implemented CODEOWNERS files that automatically route reviews to the right team. Without these guardrails, a monorepo quickly devolves into a free-for-all where anyone can break anything.