Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

PNPM monorepo deployment with docker compose

I currently have a monorepo which has the following structure:

.
├── services/
│   └── api/
│       ├── src/
│       │   └── ...
│       └── Dockerfile
├── apps/
│   └── frontend
├── packages/
│   └── ...
├── .npmrc
├── docker-compose.yaml
└── pnpm-lock.yaml

The dockerfile contains the following commands:

FROM node:18-alpine As base

RUN npm i -g pnpm

FROM base AS dependencies

WORKDIR /usr/src/app

COPY package.json ../../pnpm-lock.yaml ./
COPY ../../.npmrc ./
RUN pnpm install --frozen-lockfile --prod
...

For the API I load in the Docker file using the docker compose file with the context of ./services/api. When I try this it cannot find the files of the parent directory, this is due to some security feature of Docker. I could change the context and change the commands accordingly, but this would load in the entire codebase for the API image. This slows down building and deploying and my question would be is there any other way to structure the monorepo to support pnpm with Docker? I can't find any resources on the topic.

Best Regards

like image 294
Sebastiaan Avatar asked Oct 18 '25 12:10

Sebastiaan


1 Answers

I now implemented the Dockerfile as below. It is specific to my monorepo so it might not be useful, but I wanted to post it anyway if anyone ever runs into the same issue:

FROM node:18.10.0-alpine As base

ARG SERVICE_PATH
ARG PACKAGE_NAME
ARG PNPM_VERSION

# Install package manager
RUN --mount=type=cache,id=pnpm-store,target=/root/.pnpm-store \
    npm i --global --no-update-notifier --no-fund pnpm@${PNPM_VERSION}

# Use the node user from the image (instead of the root user)
USER node

# Get all dependencies and install
FROM base AS dependencies

WORKDIR /usr/app

COPY --chown=node:node pnpm-lock.yaml pnpm-workspace.yaml package.json .npmrc ./
COPY --chown=node:node ${SERVICE_PATH}/package.json ./${SERVICE_PATH}/package.json

RUN --mount=type=cache,id=pnpm-store,target=/root/.pnpm-store \
    pnpm install --frozen-lockfile --filter ${PACKAGE_NAME}\
    | grep -v "cross-device link not permitted\|Falling back to copying packages from store"

# Build application using all dependencies, copy necessary files
FROM dependencies AS build

WORKDIR /usr/app/${SERVICE_PATH}

COPY --chown=node:node ${SERVICE_PATH} ./

ENV NODE_ENV production
RUN pnpm build
RUN rm -rf node_modules src \
    && pnpm -r exec -- rm -rf node_modules

# Use base image for correct context, get built files from build stage
# Install only production dependencies
FROM base AS deploy

WORKDIR /usr/app

ENV NODE_ENV production

COPY --chown=node:node --from=build /usr/app .
RUN --mount=type=cache,id=pnpm-store,target=/root/.pnpm-store \
    pnpm install --frozen-lockfile --filter ${PACKAGE_NAME} --prod \
    | grep -v "cross-device link not permitted\|Falling back to copying packages from store"

ENV EXEC_PATH=${SERVICE_PATH}/dist/main.js

CMD node ${EXEC_PATH}

I also added a .dockerignore as suggested by bogdanoff, it looks like the following:

# Files
/**/*/.env
/**/*/.env.*
/**/*/README.md
/**/*/.git
/**/*/.gitignore
/**/*/npm-debug.log
/**/*/.dockerignore
/**/*/Dockerfile

# Directories
/**/*/node_modules
/**/*/test
/**/*/dist
like image 191
Sebastiaan Avatar answered Oct 20 '25 01:10

Sebastiaan



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!