How to reduce node_modules in Monorepo - only install dependencies of one subpackage?

Hi all!

I was just playing around with fly.io and I’m quite impressed how everything just works. Thanks for that! :slight_smile:
I have a question regarding my setup: I have a mono repository with npm workspaces. It looks like this (simplified):

  • apps/nextjs-client
  • apps/nestjs-server
  • packages/shared

What I want is to have the nestjs server deployed on fly. I got this working successfully, with a Dockerfile in the repo root. This is how it looks:

FROM node:16-alpine AS builder

ENV NODE_ENV build

USER node
WORKDIR /home/node

COPY --chown=node ./ ./

RUN npm ci
RUN cd apps/nestjs-server && npm run build

FROM node:16-alpine

ENV NODE_ENV production

USER node
WORKDIR /home/node

COPY --from=builder /home/node/apps/nestjs-server/package*.json /home/node/
COPY --from=builder /home/node/apps/nestjs-server/dist/ /home/node/dist/
COPY --from=builder /home/node/node_modules/ /home/node/node_modules/

EXPOSE 3000

CMD ["node", "dist/main.js"]

In the builder, I’m copying the whole monorepo and installing all dependencies. This is necessary, that the server package finds the local shared package. After that, I build and copy only the nestjs server dist folder, package.json and node_modules.
The problem here: node_modules contains all dependencies from all apps and packages. It more than 500MB. But I only want to have the node_modules that are required in from the nestjs production build. Is there a way to only have those in the final image?

Best,
Fabian

Is there maybe another node_modules in /home/node/apps/nestjs-server/? Could you just copy that one?

I’m using npm workspaces (with turborepo, but that should not be that relevant) which installs all dependencies into the root node_modules folder.
One thing I tried was to copy the nestjs-server and shared folder only, and link them with a local npm install. There is a blog post about technique that goes more into detail: Sharing Code in a Node Monorepo with Docker - lucas.love The problem with this approach is that I now need to manually mirror parts my monorepo. Also, global tooling like turborepo won’t work anymore, so this is something I try to avoid.

I’m a bit wondering if this is really a problem.

Yeah, this is a problem in general with node and monorepos. The manual approach is what I’ve seen work for for projects like RedwoodJS. See our Dockerfile for that here: flyctl/Dockerfile at master · superfly/flyctl · GitHub.

For me, the staggering number of options for managing this stuff under Node makes it hard to know what the right approach is!

1 Like