I'm just trying to switch to pnpm and I'm a bit lost on how to dockerize packages in my workspace, something that I thought would be a common thing to do.

The example in their docs [1] seems to just be wrong

> FROM common AS app1

> COPY --from=prod-deps /app/packages/app1/node_modules/ /app/packages/app1/node_modules

> COPY --from=build /app/packages/app1/dist /app/packages/app1/dist

This of course will not work, since the node_modules folders of packages in a pnpm workspace just contain symlinks to the virtual store at the root of the monorepo, not the actual modules. I don't see any good way to do what this example pretends to achieve (minimize docker image build size) since even when hoisting is forced, pnpm seems to only ever hoist node_modules in the root of the repo.

Sorry for this somewhat unrelated rant, I was just surprised at hitting such an obstacle immediately after trying to adopt pnpm after I heard so much praise for this tool.

[1] https://pnpm.io/docker#example-2-build-multiple-docker-image...

Checkout Depot's example Dockerfile for node + pnpm: https://depot.dev/docs/languages/node-pnpm-dockerfile which is best-practices based even if you're not using Depot. They also explain each of the lines in the Dockerfile.

As someone struggling with Docker caching at work at the moment, I think your problem is less with pnpm and more with the difficulty of writing decent Dockerfiles.

Thank you, but I think that’s a bit different since the example is not using workspaces, which makes it a lot simpler

> COPY --from=deps /app/node_modules /app/node_modules

This won’t work in the case of the workspace setup since the node_modules folder for my app just contains symlinks, not the actual node_modules, that’s the essential problem I’m facing.

Right, what the Dockerfile is doing is building the store first inside the image using pnpm fetch. Then, when the Dockerfile does pnpm install inside the image, the resulting links in the generated node_modules are pointing to the store path inside the image. When you do COPY . ., either your .gitignore file is already ignoring node_modules or you have a separate .dockerignore file which should also exclude node_modules; thus, when copying files into the image, you're never copying the node_modules folder and thus links inside the node_modules folder to places on your host laptop are irrelevant.
Hm, thank you for the explanation but I'm still not 100% sure how it would apply.

> thus links inside the node_modules folder to places on your host laptop are irrelevant

I'm not talking about the host computer at any point of this, this is about the workspace setup, all of which happens inside the image, since packages within the workspace depend on each other the whole workspace needs to be built together in the image.

I am willing to admit that I am missing something but I'm just not sure what exactly.

Given a workspace layout like this:

    ├── Dockerfile  
    ├── .dockerignore  
    ├── .gitignore  
    ├── packages/  
    │   ├── app1/  
    │   │   ├── dist/  
    │   │   ├── package.json  
    │   │   ├── src/  
    │   │   └── tsconfig.json  
    │   ├── app2/  
    │   │   ├── dist/  
    │   │   ├── package.json  
    │   │   ├── src/  
    │   │   └── tsconfig.json  
    │   └── common/  
    │       ├── dist/  
    │       ├── package.json  
    │       ├── src/  
    │       └── tsconfig.json
Whenever pnpm installs workspace dependencies, it installs them at the root of the workspace, (in the store inside the docker build image of course, not on the host), and those are the dependencies for all 3 packages all together in one virtual store, here:

./node_modules/.pnpm

So, when I want to create my container image for say packages/app1, I don't see how I could copy only my dependencies for that app from the build image like this:

COPY --from=deps /app/packages/app2/node_modules/ /app/packages/app2/node_modules

Because while of course the dependencies are installed in the build image, they are at the root of the workspace in virtual store there, and not in /app/packages/app2/node_modules/ – this directory only contains symlinks to the root virtual store.

Of course I can copy all the dependencies from the root virtual store of the build image into my image, but then those are the dependencies for ALL packages in the workspace, not just for app1

I suppose I could try to install only the dependencies for app1, but this is broken with the default pnpm settings at the moment (it still installs dependencies for everything in the workspace)

https://github.com/pnpm/pnpm/issues/6300

In the end, I think the cleanest way would indeed be to just bundle the dependencies into each app during build.

An update on this:

After talking to one of the contributors on Discord, it seems that they have a special "deploy" command for exactly this (copying files and dependencies for a single workspace package) which I had overlooked since the documentation for it wasn't so self explanatory, they have now updated the docs for this command [1] and opened a PR to update the docs for the Monorepo Docker example to use it instead [2].

I have to say I'm impressed with how responsive the maintainers were to my question, and this `pnpm deploy` workflow does actually make sense to me.

[1] https://github.com/pnpm/pnpm.io/commit/3e6cb7b2cdaf23a423c31...

[2] https://github.com/pnpm/pnpm.io/pull/469