Docker builds for a monorepo environment Docker builds for a monorepo environment docker docker

Docker builds for a monorepo environment


Every package.json file needs to list the complete immediate dependencies of its application. I should be able to check out your source repository, run yarn install, and have a working application tree. Where your question says "and by the way these other dependencies are installed in the environment and I just assume them", that's a problem for anyone who isn't working on the exact system you are, and it's more specifically a problem for Docker and other automated-build tools.

Your library dependencies can have their own library dependencies. These will get listed out in the yarn.lock file, but they don't need to be directly listed in the package.json file.

Taking the database-access libraries as an example: if your main application uses them, they need to be included in your dependencies. But if all of the database access is encapsulated in your common shared library, your applications only need to refer to that library (in foo/package.json), and the library needs to include the database dependencies (in common/package.json).

You should split dependencies from devDependencies. The things you need to run your application (express) need to be listed in dependencies; the things you only need to build your application (eslint) should be devDependencies. You discuss image size; this gives you a way to install a much smaller group of packages in the container when you actually run it.

(Note that Yarn doesn't actually support not installing devDependencies; npm does, though it's otherwise much slower to use.)

Then a multi-stage build can produce a smaller image. The idea here is that the first stage installs the entire dev dependencies, and builds the application; the second stage only includes the runtime dependencies and the built code. This more or less looks like:

ARG node_version=12-currentFROM node:${node_version} AS buildWORKDIR /appCOPY package.json yarn.lock .RUN yarn install --immutableCOPY . .RUN yarn buildFROM node:${node_version}WORKDIR /appENV NODE_ENV=productionCOPY package.json yarn.lock .RUN yarn install --immutable# RUN npm ci  # in production mode, skips devDependenciesCOPY --from=build /app/dist distCMD ["node", "/app/dist/main.js"]

You shouldn't ever need a "builder container"; the setup you show is basically identical to a multi-stage build, except spread across three separate Dockerfiles. In particular if you have an image that doesn't run a command but just contains files, it's a good candidate to be an early stage in a multi-stage build.