Docs / Programming & Development / Monorepo with Turborepo

Monorepo with Turborepo

By Admin · Mar 15, 2026 · Updated Apr 23, 2026 · 271 views · 3 min read

Turborepo is a high-performance build system for JavaScript/TypeScript monorepos that provides intelligent caching, parallel execution, and minimal configuration. It is ideal for managing multiple applications and packages in a single repository. This guide covers setting up, structuring, and deploying a Turborepo monorepo.

Project Setup

# Create new Turborepo
npx create-turbo@latest my-monorepo
cd my-monorepo

# Or add to existing project
npm install turbo --save-dev

Project Structure

my-monorepo/
├── apps/
│   ├── web/              # Next.js frontend
│   │   ├── package.json
│   │   └── src/
│   ├── api/              # Express/Fastify backend
│   │   ├── package.json
│   │   └── src/
│   └── mobile/           # React Native app
│       └── package.json
├── packages/
│   ├── ui/               # Shared UI components
│   │   ├── package.json
│   │   └── src/
│   ├── config/           # Shared configs (ESLint, TS)
│   │   ├── eslint/
│   │   └── typescript/
│   ├── database/         # Shared Prisma schema
│   │   ├── package.json
│   │   └── prisma/
│   └── utils/            # Shared utilities
│       ├── package.json
│       └── src/
├── turbo.json
├── package.json
└── pnpm-workspace.yaml

Turborepo Configuration

// turbo.json
{
  "$schema": "https://turbo.build/schema.json",
  "globalDependencies": ["**/.env.*local"],
  "tasks": {
    "build": {
      "dependsOn": ["^build"],
      "outputs": ["dist/**", ".next/**", "!.next/cache/**"]
    },
    "test": {
      "dependsOn": ["build"],
      "inputs": ["src/**/*.tsx", "src/**/*.ts", "test/**/*.ts"]
    },
    "lint": {
      "dependsOn": ["^build"]
    },
    "dev": {
      "cache": false,
      "persistent": true
    },
    "db:generate": {
      "cache": false
    },
    "deploy": {
      "dependsOn": ["build", "test", "lint"]
    }
  }
}

Workspace Configuration

# pnpm-workspace.yaml
packages:
  - "apps/*"
  - "packages/*"

# Root package.json
{
  "name": "my-monorepo",
  "private": true,
  "scripts": {
    "build": "turbo build",
    "dev": "turbo dev",
    "test": "turbo test",
    "lint": "turbo lint",
    "clean": "turbo clean"
  },
  "devDependencies": {
    "turbo": "^2.0.0"
  },
  "packageManager": "pnpm@9.0.0"
}

Shared Package Example

// packages/ui/package.json
{
  "name": "@myorg/ui",
  "version": "0.0.1",
  "main": "./src/index.ts",
  "types": "./src/index.ts",
  "exports": {
    ".": "./src/index.ts",
    "./button": "./src/button.tsx"
  },
  "dependencies": {
    "react": "^18.3.0"
  }
}

// packages/ui/src/button.tsx
export function Button({ children, onClick }: { children: React.ReactNode; onClick?: () => void }) {
  return <button className="btn-primary">{children}</button>;
}

// Consume in apps/web
import { Button } from "@myorg/ui/button";

Remote Caching

# Enable Vercel Remote Cache (free for Turborepo)
npx turbo login
npx turbo link

# Or self-hosted cache with ducktape
# https://github.com/ducktors/turborepo-remote-cache
docker run -p 3000:3000 ducktors/turborepo-remote-cache

# turbo.json add:
# "remoteCache": { "enabled": true }

# Benefits:
# - CI builds reuse local dev cache and vice versa
# - Team members share cache across machines
# - Typical 60-90% build time reduction

CI/CD Pipeline

# .github/workflows/ci.yml
name: CI
on: [push, pull_request]
jobs:
  build:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: pnpm/action-setup@v4
      - uses: actions/setup-node@v4
        with:
          node-version: 20
          cache: "pnpm"

      - run: pnpm install --frozen-lockfile

      # Run only affected packages
      - run: pnpm turbo build test lint --filter="...[HEAD~1]"

      # Or build everything with cache
      - run: pnpm turbo build test lint
        env:
          TURBO_TOKEN: ${{ secrets.TURBO_TOKEN }}
          TURBO_TEAM: ${{ vars.TURBO_TEAM }}

Deploying Individual Apps

# Deploy only the web app
turbo build --filter=web

# Docker for specific app
# apps/web/Dockerfile
FROM node:20-alpine AS builder
RUN corepack enable pnpm
WORKDIR /app
COPY . .
RUN pnpm install --frozen-lockfile
RUN pnpm turbo build --filter=web

FROM node:20-alpine
WORKDIR /app
COPY --from=builder /app/apps/web/.next ./.next
COPY --from=builder /app/apps/web/package.json ./
COPY --from=builder /app/node_modules ./node_modules
CMD ["pnpm", "start"]

Summary

Turborepo transforms monorepo management from a build-time headache into a productivity advantage. Its intelligent caching ensures you never rebuild unchanged packages, while remote caching shares build artifacts across your entire team and CI. The task dependency graph automatically determines build order, and filtering lets you build only affected packages. For TypeScript projects with shared code between frontend, backend, and packages, Turborepo is the most efficient build system available.

Was this article helpful?