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
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
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With