Docker
Purpose and Scope
This page explains the Docker-oriented workflow that Turborepo documents for monorepos: use turbo prune with the --docker flag to make Docker builds depend on only the packages, package manifests, and lockfile entries needed by the application being deployed. The guide is written for teams that already have, or are creating, a repository shaped like a typical create-turbo workspace, where deployable apps live under apps and shared code lives in internal packages. The goal is not to replace Docker best practices; it is to prevent monorepo-wide dependency churn from invalidating otherwise reusable Docker layers.
Sources: apps/docs/content/docs/guides/tools/docker.mdx, apps/docs/content/docs/guides/tools/create-turbo-callout.tsx
The Docker integration belongs to Turborepo’s broader tool-guide section, alongside Biome, ESLint, Jest, TypeScript-oriented tools, and other development tooling. That placement matters because Docker is treated as another tool that should be configured around Turborepo’s task graph and cache model rather than as a special deployment-only concern. The surrounding guides establish a consistent pattern: introduce the tool’s role, explain the monorepo-specific tradeoff, then show how Turborepo configuration or commands make the tool faster and more predictable. For Docker, the important command is turbo prune api --docker, not a turbo.json task definition.
Sources: apps/docs/content/docs/guides/tools/index.mdx, apps/docs/content/docs/guides/tools/biome.mdx, apps/docs/content/docs/guides/tools/jest.mdx
Relevant Source Files
apps/docs/content/docs/guides/tools/docker.mdx- First-party Docker integration guide; defines the monorepo lockfile problem, the example Dockerfile,.dockerignore,turbo prune api --docker, and theout/jsonplusout/fullsplit used for Docker layer optimization.apps/docs/content/docs/guides/tools/create-turbo-callout.tsx- Shared callout component used by tool guides to state that examples assumecreate-turboor a similarly structured repository.apps/docs/content/docs/guides/tools/index.mdx- Tool-guide overview that lists Docker as one of the supported integration topics and frames Turborepo as compatible with common developer tooling.apps/docs/content/docs/guides/tools/biome.mdx- Neighboring tool guide that demonstrates the section’s pattern of explaining tool-specific caching tradeoffs before giving configuration.apps/docs/content/docs/guides/tools/jest.mdx- Neighboring tool guide that shows how Turborepo differentiates cacheable finite tasks from persistent watch processes, a useful contrast for Docker builds.apps/docs/content/docs/guides/tools/meta.json- Navigation metadata for the tool-guide section.
The Monorepo Docker Problem
A conventional Node Dockerfile usually copies the root package.json, the root lockfile, the target app’s package.json, installs dependencies, then copies the application source. Docker’s layer cache makes this efficient when those dependency inputs are stable: if the manifest and lockfile inputs do not change, npm install can be skipped by reusing the previous layer. In a single-package repository, that is usually enough. In a monorepo, however, the root lockfile describes dependency resolution for every workspace, not only the service you are deploying.
Sources: apps/docs/content/docs/guides/tools/docker.mdx
The documented example uses an apps/api deployment beside an apps/web workspace. The Dockerfile for apps/api copies the monorepo root package.json, package-lock.json, and apps/api/package.json, then runs npm install. That means a dependency added to apps/web can modify the global package-lock.json, which causes Docker to invalidate the dependency layer for the unrelated API image. The app code may be unchanged, but the lockfile input changed, so the image performs install work and can trigger redeploys anyway.
Sources: apps/docs/content/docs/guides/tools/docker.mdx
This is the same class of problem Turborepo is designed to avoid elsewhere: unnecessary work caused by inputs that are broader than the actual unit being built. Docker already tries to do as little work as possible, but it can only compare the files copied into each layer. If the copied lockfile contains the whole workspace’s dependency graph, Docker cannot infer that a changed entry is irrelevant to the target application. Turborepo’s contribution is to produce a smaller build context whose package graph and lockfile are already scoped to the selected target.
Sources: apps/docs/content/docs/guides/tools/docker.mdx
Pruning for Docker
The documented solution is to run turbo prune for the package you intend to deploy, with --docker enabled. In the guide’s example, the target package is api, so the command is turbo prune api --docker. The command creates a pruned version of the monorepo under ./out. It includes only the workspaces that api depends on and creates a lockfile reduced to the dependencies relevant to that target. The result is still a monorepo-shaped directory, but it is a deployment-focused slice rather than the entire repository.
Sources: apps/docs/content/docs/guides/tools/docker.mdx
turbo prune api --dockerWithout the Docker flag, turbo prune places the relevant files together under ./out. With --docker, the output is arranged to support Docker’s dependency-layer caching strategy. The guide calls out ./out/json as the first-stage input: it contains only the package metadata needed to install dependencies, such as the root package.json and the target app’s package.json. The ./out/full directory then contains the full source code for the pruned workspace slice. This split lets a Dockerfile copy metadata first, install dependencies, and copy source later.
Sources: apps/docs/content/docs/guides/tools/docker.mdx
The practical effect is that source-only changes in the target package do not force a dependency reinstall, and dependency changes in unrelated workspaces do not force a reinstall for the target image. The pruned lockfile is the key improvement over copying the repository’s global lockfile. Docker still decides whether a layer is reusable by comparing files, but those files now represent only the target’s dependency closure. In large repositories, that reduces avoidable image rebuilds, shortens CI time, and limits deployment fan-out when teams make independent changes in different applications.
Sources: apps/docs/content/docs/guides/tools/docker.mdx
Dockerfile Flow
Start with a .dockerignore that prevents local dependency directories and debug logs from entering the build context. The Docker guide explicitly recommends excluding node_modules and npm-debug.log in the introductory example. That recommendation remains valid when using pruning: the Docker image should install dependencies from the pruned manifests and lockfile rather than copying a developer’s local installation. Keeping the context clean also makes Docker cache invalidation easier to reason about, because the copied files are intentional build inputs rather than incidental local artifacts.
Sources: apps/docs/content/docs/guides/tools/docker.mdx
node_modules
npm-debug.logA Docker-oriented pruned build normally has three conceptual phases. First, run turbo prune <package> --docker before building the image, either locally, in CI, or inside an earlier Docker stage if your build is arranged that way. Second, copy out/json into the image and run the package manager install command, so that the dependency layer is based on pruned package metadata. Third, copy out/full and run the application build or start command. The exact package manager command depends on your repository, but the layer order is the important Turborepo-specific idea.
Sources: apps/docs/content/docs/guides/tools/docker.mdx
The guide’s initial unpruned Dockerfile uses Node, sets WORKDIR /usr/src/app, copies the root manifest and lockfile, copies apps/api/package.json, runs npm install, copies the app source, exposes port 8080, and starts node apps/api/server.js. With pruning, the dependency-copy portion should be narrowed to the files from out/json, and the source-copy portion should use the pruned source from out/full. That preserves Docker’s natural install-before-source layering while replacing monorepo-wide inputs with target-specific inputs.
Sources: apps/docs/content/docs/guides/tools/docker.mdx
# 1. Copy pruned package metadata from out/json
# 2. Install dependencies
# 3. Copy pruned source from out/full
# 4. Build or start the target appSystem-to-Code Mapping
The Docker guide is a first-party MDX document with frontmatter identifying it as a Turborepo integration page. Its summary is precise: use turbo prune to create optimized Docker images from a monorepo with minimal dependencies. The guide imports CreateTurboCallout, which renders a shared info callout explaining that the instructions assume create-turbo or a similarly structured repository. That makes the examples intentionally concrete without requiring Docker support to be tied to only one template or package manager.
Sources: apps/docs/content/docs/guides/tools/docker.mdx, apps/docs/content/docs/guides/tools/create-turbo-callout.tsx
The tool index provides the higher-level navigation surface for this page. It says Turborepo works with common tooling and exposes cards for Docker, Biome, ESLint, Jest, Oxc, Prisma, shadcn/ui, Storybook, TypeScript, and Vitest. That index confirms Docker is documented as part of day-to-day monorepo tooling rather than only as a reference command. The neighboring Biome and Jest guides also show the documentation style: explain the tool’s runtime behavior, then decide how Turborepo should cache, parallelize, or isolate work around that behavior.
Sources: apps/docs/content/docs/guides/tools/index.mdx, apps/docs/content/docs/guides/tools/biome.mdx, apps/docs/content/docs/guides/tools/jest.mdx
Compact Reference
| Item | Docker-guide behavior |
|---|---|
| Primary command | turbo prune api --docker in the documented example |
| Target argument | The package or app to deploy, such as api |
| Default output root | ./out |
| Docker metadata output | ./out/json, used for package installation inputs |
| Docker source output | ./out/full, used for the pruned source tree |
| Key benefit | Avoids reinstalling or redeploying a target because an unrelated workspace changed the global lockfile |
| Context assumption | create-turbo or a similarly structured monorepo |
Sources: apps/docs/content/docs/guides/tools/docker.mdx, apps/docs/content/docs/guides/tools/create-turbo-callout.tsx
Use turbo prune --docker when the deployable unit is smaller than the repository and Docker dependency layers are being invalidated by unrelated workspace changes. Do not treat the output as a replacement for understanding your package manager or application build; it is a narrowed input tree for Docker. After pruning, inspect out/json and out/full to confirm that the expected package manifests, internal dependencies, and source files are present for the selected target. Then adapt your Dockerfile so dependency installation happens after copying out/json and before copying out/full.