Structuring a Repository

Purpose and Scope

Turborepo expects a repository to follow normal JavaScript package manager workspace conventions before it can do its own higher-level orchestration well. A workspace is the package manager feature that groups multiple packages in one repository, and a multi-package workspace is the monorepo shape most Turborepo guides assume. This page explains the repository shape that lets package managers, editors, TypeScript, CI, and Turborepo agree on where packages live, how they depend on each other, and which scripts can be scheduled as tasks.

Sources: apps/docs/content/docs/crafting-your-repository/structuring-a-repository.mdx

The practical goal is not to invent a Turborepo-only layout. The first-party guide emphasizes leaning on ecosystem conventions so existing tools keep working and so an existing repository can adopt Turborepo incrementally. Turborepo then layers task graph execution, caching, development process management, and CI acceleration on top of the package graph that the workspace already describes. Treat the structure as the contract between the package manager and Turborepo: once packages are discoverable, tasks can be configured centrally and run consistently across applications and libraries.

Sources: apps/docs/content/docs/crafting-your-repository/structuring-a-repository.mdx, apps/docs/content/docs/crafting-your-repository/configuring-tasks.mdx

Relevant Source Files

  • apps/docs/content/docs/crafting-your-repository/structuring-a-repository.mdx - Defines the workspace-oriented guide, starter commands, anatomy of a multi-package repository, and the expected root files and package directories.
  • apps/docs/content/docs/crafting-your-repository/caching.mdx - Explains why deterministic task outputs and declared inputs and outputs matter after the repository has buildable packages.
  • apps/docs/content/docs/crafting-your-repository/configuring-tasks.mdx - Shows how the root task configuration registers scripts, orders work with dependencies, and makes the structured workspace executable through Turborepo.
  • apps/docs/content/docs/crafting-your-repository/constructing-ci.mdx - Connects repository structure to CI usage, remote caching variables, filtering, affected work, and Docker-oriented workflows.
  • apps/docs/content/docs/crafting-your-repository/creating-an-internal-package.mdx - Demonstrates how a new package directory becomes an internal package with a package manifest, exports, scripts, and workspace dependencies.
  • apps/docs/content/docs/crafting-your-repository/developing-applications.mdx - Describes how application layout supports long-running development tasks, setup tasks, filtering, terminal UI interaction, and watch-oriented workflows.

Workspace Anatomy

A typical Turborepo workspace starts with root-level project metadata, one lockfile, a workspace declaration when the package manager requires it, and a root Turborepo configuration. The guide’s starter shape places deployable applications under an apps directory and shared packages under a packages directory, with each application or library owning its own package manifest. For pnpm, the highlighted root files include the manifest, lockfile, workspace file, and Turborepo configuration; for Yarn, npm, and Bun, the lockfile names differ while the package directory pattern stays the same.

Sources: apps/docs/content/docs/crafting-your-repository/structuring-a-repository.mdx

The root manifest and lockfile should be treated as infrastructure for the whole workspace, not as incidental files. The package manager uses them to install one coherent dependency graph, and Turborepo uses the packages it discovers to understand which work can run where. The docs deliberately distinguish single-package workspaces from multi-package workspaces; this guide focuses on the multi-package case because task scheduling and internal package relationships become most valuable when applications and shared libraries live together and can be changed in the same commit.

Sources: apps/docs/content/docs/crafting-your-repository/structuring-a-repository.mdx, apps/docs/content/docs/crafting-your-repository/creating-an-internal-package.mdx

A useful starting layout is:

package.json
pnpm-lock.yaml or yarn.lock or package-lock.json or bun.lock
pnpm-workspace.yaml when using pnpm
turbo.json
apps/
  docs/package.json
  web/package.json
packages/
  ui/package.json

The exact top-level folder names are conventions rather than magic names, but the convention is valuable because it communicates intent. Applications are entry points that are built, developed, tested, and deployed. Shared packages are reusable units such as UI libraries, TypeScript configuration, ESLint configuration, or domain utilities. Keeping those roles visible makes later task design easier: applications often depend on packages, packages may build before applications, and filters can target a specific application while still including the dependency work required to make it run correctly.

Sources: apps/docs/content/docs/crafting-your-repository/structuring-a-repository.mdx, apps/docs/content/docs/crafting-your-repository/developing-applications.mdx

Packages, Internal Libraries, and Naming

An internal package becomes part of the workspace when it has a directory and a package manifest that the workspace configuration can discover. The internal package guide creates a packages/math directory, adds a manifest, gives the package a name, and then uses that name as the import identity for the rest of the workspace. That naming choice is important: if the manifest names the package as a scoped module, other packages import that exact name, and Turborepo can infer package graph relationships from dependency declarations in manifests.

Sources: apps/docs/content/docs/crafting-your-repository/creating-an-internal-package.mdx

A maintainable repository structure therefore includes both physical organization and dependency declarations. The folder tells maintainers where to find a package, while the manifest tells tools how it participates in the graph. Internal packages may also expose compiled outputs through package exports and include scripts such as development and build scripts. Those scripts are not only local conveniences; once their names match tasks in the root configuration, Turborepo can schedule them across packages, run dependent library builds before application builds, and reuse outputs when caching is configured.

Sources: apps/docs/content/docs/crafting-your-repository/creating-an-internal-package.mdx, apps/docs/content/docs/crafting-your-repository/configuring-tasks.mdx

A compact internal package manifest commonly includes a name, module type, scripts, exports, and workspace dependencies:

{
  "name": "@repo/math",
  "type": "module",
  "scripts": {
    "dev": "tsc --watch",
    "build": "tsc"
  },
  "devDependencies": {
    "@repo/typescript-config": "workspace:*",
    "typescript": "latest"
  }
}

Task Configuration and Repository Shape

After packages are discoverable, the root configuration turns package scripts into a repository workflow. The configuring tasks guide defines a task as a script that Turborepo runs, and each key in the tasks object can be executed by the run command. Turborepo searches package manifests for scripts with matching names. If a build task is registered without dependencies or outputs, Turborepo can run matching scripts, but the guide warns that this is incomplete because ordering and caching behavior have not been described yet.

Sources: apps/docs/content/docs/crafting-your-repository/configuring-tasks.mdx

The first important relationship is build order. A common monorepo expectation is that libraries build before applications that consume them. Turborepo expresses that relationship with a dependency entry that points to the same task in package dependencies. This connects repository structure to execution semantics: because applications declare dependencies on internal packages, Turborepo can derive which build tasks must happen first. The result is not a serial workspace run; Turborepo parallelizes any work it safely can, while respecting the graph edges that protect dependent packages from building too early.

Sources: apps/docs/content/docs/crafting-your-repository/configuring-tasks.mdx

A minimal root task configuration for that relationship looks like:

{
  "tasks": {
    "build": {
      "dependsOn": ["^build"],
      "outputs": ["dist/**"]
    }
  }
}

Outputs are part of the repository contract as well. The caching guide explains that Turborepo restores task results from cache using a fingerprint of known inputs. That only works correctly when tasks are deterministic and when outputs are identified for cacheable work. A well-structured repository therefore pairs folders and manifests with explicit task configuration. Without that pairing, a package may be discoverable but inefficient; with it, repeated builds can restore previous results from local cache or, when enabled, from a remote cache shared by teammates and CI.

Sources: apps/docs/content/docs/crafting-your-repository/caching.mdx, apps/docs/content/docs/crafting-your-repository/configuring-tasks.mdx

Development and CI Implications

Development workflows benefit from the same structure, but long-running tasks need different configuration than build tasks. The developing applications guide recommends registering development tasks with caching disabled and persistence enabled, because development servers, watch compilers, and similar processes are expected to keep running and react to source changes. The persistent setting also prevents accidentally depending on a task that will not exit. Once registered, the same task can run across the workspace or be filtered to a specific application and its required context.

Sources: apps/docs/content/docs/crafting-your-repository/developing-applications.mdx

{
  "tasks": {
    "dev": {
      "cache": false,
      "persistent": true
    }
  }
}
turbo dev --filter=web

CI should use the same mental model as local development: run registered tasks through Turborepo, then add CI-specific cache and selection behavior. The constructing CI guide highlights remote caching through environment variables for the remote cache token and team, and it notes that filters work in CI just as they do locally. Repository structure matters here because filters can select packages, directories, or history-based changes only when the workspace graph and source history are available. Shallow clones may limit history-based filtering, so CI checkout depth is a structural pipeline choice.

Sources: apps/docs/content/docs/crafting-your-repository/constructing-ci.mdx

Start new work with a generated starter when possible, then inspect the resulting shape instead of copying individual files blindly. The docs recommend create-turbo for new monorepo users because it produces a valid workspace structure immediately. For an existing repository, first confirm that the package manager recognizes every intended application and shared package. Next, ensure each package has a meaningful name and scripts. Then add or refine the root Turborepo configuration so common tasks, dependency ordering, outputs, and long-running development behavior are explicit.

Sources: apps/docs/content/docs/crafting-your-repository/structuring-a-repository.mdx, apps/docs/content/docs/crafting-your-repository/configuring-tasks.mdx, apps/docs/content/docs/crafting-your-repository/developing-applications.mdx

Common starter commands are:

pnpm dlx create-turbo@latest
yarn dlx create-turbo@latest
npx create-turbo@latest
bunx create-turbo@latest

Use this checklist when reviewing a repository:

  • The root has a package manifest, one package-manager lockfile, and the workspace declaration required by the selected package manager.
  • Application packages and shared packages each have their own package manifest.
  • Internal packages use stable names that match how other packages import them.
  • Shared packages declare scripts that can be matched by root Turborepo tasks.
  • Build tasks declare dependency order and outputs before teams rely on caching.
  • Development tasks are marked as long-running and non-cacheable when appropriate.
  • CI uses the same registered tasks, plus remote cache credentials and filters where useful.

Next Steps

Once the workspace is structured, continue with dependency management and internal package design before optimizing execution. The structuring guide’s related material points toward managing dependencies, internal packages, and package types; the adjacent crafting guides then show how to configure tasks, use caching, develop applications, and construct CI. That sequence is deliberate. A clear layout gives Turborepo a package graph, internal package manifests give that graph meaningful edges, task configuration turns scripts into workflows, and caching plus CI configuration make those workflows fast for everyone on the team.

Sources: apps/docs/content/docs/crafting-your-repository/structuring-a-repository.mdx, apps/docs/content/docs/crafting-your-repository/creating-an-internal-package.mdx, apps/docs/content/docs/crafting-your-repository/caching.mdx, apps/docs/content/docs/crafting-your-repository/constructing-ci.mdx