How to build a docker image from a nodejs project in a monorepo with yarn workspaces
Master System Design with Codemia
Enhance your system design skills with over 120 practice problems, detailed solutions, and hands-on exercises.
Introduction
Building one application from a Yarn-workspace monorepo is mostly about getting the Docker build context right. The image has to see the root lockfile, the workspace manifests that affect dependency resolution, and the application source for the specific package you want to build.
Why Monorepos Need a Different Dockerfile
In a single-package Node.js project, the Dockerfile can usually copy one package.json, install dependencies, copy source, and build. In a monorepo, dependencies are resolved from the repository root, and one workspace may depend on another internal package. If you only copy the target app folder, yarn install will not have enough information to resolve the workspace graph.
Assume this repository layout:
If apps/api depends on packages/shared, the Docker build must include both workspace manifests before installing dependencies.
A Multi-Stage Dockerfile
The following example works well for a monorepo that builds one service named @acme/api:
This Dockerfile has three stages:
- '
depsinstalls dependencies using the root lockfile and the workspace manifests' - '
buildcopies the full repository and compiles the target workspace' - '
runtimekeeps only what is needed to start the service'
That structure improves build caching because dependency installation is invalidated only when the relevant manifest files change.
Build From the Monorepo Root
The build context should be the repository root, not the app directory. From the root of the monorepo:
The trailing . matters because it makes the entire repository available to the Docker daemon. If you run docker build from apps/api, the workspace dependency files outside that folder will be invisible unless you restructure the build.
A .dockerignore file also helps keep the context lean:
Exclude large generated folders, but do not accidentally ignore workspace sources that the target app needs.
Yarn Version Differences
The example above assumes a classic node_modules install flow. If your monorepo uses Yarn Berry with Plug'n'Play or workspaces focus, the runtime stage may look different. The general rule still holds: copy the root dependency metadata first, then the relevant workspace manifests, then the source code.
If you are building only one deployable app from a very large monorepo, specialized tools such as Turborepo pruning or Nx project graph pruning can reduce the Docker context further. That is an optimization step, not a requirement for correctness.
Common Pitfalls
- Building from the app folder instead of the monorepo root hides sibling workspaces from Docker.
- Copying source before dependency manifests destroys build-cache efficiency.
- Forgetting internal workspace packages causes
yarn installor the build step to fail. - Carrying the entire repository into the runtime image makes the final image larger than necessary.
- Using Yarn Berry features without copying the related config files, such as
.yarnrc.yml, leads to confusing install errors.
Summary
- Build from the monorepo root so Docker can see the full workspace graph.
- Copy the root lockfile and relevant workspace manifests before running
yarn install. - Use a multi-stage Dockerfile to separate dependency install, build, and runtime concerns.
- Keep the runtime image small by copying only the built app and required dependencies.
- Adapt the final stage to your Yarn version, but keep the overall build order the same.

