Build Pnpm Workspace Monorepos with Docker Example Guide

Posted November 12, 2023
Build a Monorepos with Docker; A Pnpm with Workspace Example Guide

This guide creates a workflow for setting up a Monorepo with a pnpm-workspace.yaml file to manage shared dependencies with Docker. You will use Pnpm Workspace as your Monorepo control system. You will then use Docker to Compose a Dockerfile to install dependencies and package the application into a Docker image.

You will learn the following:

  • Initialize Monorepo with Pnpm
  • Create Package Configurations
  • Define Dependencies in Monorepo
  • Create Docker Configuration
  • Build and run your Monorepo with Docker

Install the latest Node.js on your local computer to proceed with this guide.

Now, dive in and learn how to set up a Pnpm Monorepo with Workspace and dockerizing it.

Related: Complete Guide to Pnpm and Docker with TypeScript & Monorepo

Initialize Monorepo with Pnpm

  • Ensure you have Pnpm installed:
npm i -g pnpm
  • Create a directory to host the project
mkdir monorepo_demo
  • Proceed to your desired directory and initialize the monorepo
cd monorepo_demo && pnpm init

In this project, we will be using Express across the packages we will have. To start, we will install Express on the root project directory as below:

pnpm install express

The next step is to set up the different packages we will have.

Create Monorepo Package Configurations using Pnpm with Workspace

In the project root directory, create a packages directory. This step will create Monorepo using Pnpm with Workspace.

mkdir packages

In the project directory, we will need to define the package directory in a configuration. To do so, create a pnpm-workspace.yml file:

packages:
    - "./packages/**"

We will have two directories inside the packages folder: main and shared. We will have an express application running on the main directory with a dependency on the shared directory.

  • Create the shared directory.
mkdir shared
  • Proceed to the shared directory and initiliaze pnpm to create a package.json:
cd shared && pnpm init
  • In the root of the shared directory, create an index.js file. The file will host a function for outputting a JSON message as below:
module.exports.testConnection = () => ({
    "message":"Connection is good."
});
  • Back to the project repository, and create a main directory.
mkdir main && cd main
  • Initialize pnpm:
pnpm init

Create an app.js file in the project root directory. In the app.js file, configure a minimalistic express application as below:

const express = require('express');
const app = express();

const PORT = process.env.PORT ?  process.env.PORT : 4000;


app.get("/",(req,res) => res.json({
    success:true,
    message:"Hello world."
}));


app.listen(PORT, () => console.log("App started and running on PORT "+PORT));
  • In the package.json file, add a script for running it:
"scripts": {
    "start": "node app.js"
}

Define Dependencies Workspace in Monorepo

Now, let’s get the Monorepo Workspace ready. For this, use Pnpm as follows:

  • From the terminal of the main directory, import the shared package by running this command:
pnpm add @monorepo_demo/shared
  • Once the above command has finalized, on your package.json file you should have @monorepo_demo/shared added.

To utilize this shared dependency, on the app.js:

  • import the shared package:
const {testConnection} = require("@monorepo_demo/shared");
  • On the message returned on the default GET route, change it to be from the testConnection function:
app.get("/",(req,res) => res.json({
    success:true,
    ...testConnection()
}));
  • Run your application:
pnpm start

Build a Monorepos with Docker; A Pnpm with Workspace Example Guide

Creating Your Pnpm Monorepo for Workspace Docker Configuration

Here comes the fun part: dockerizing your PNPM Monorepo with Docker. Now let’s put it together - pnpm, Monorepo, and Docker.

Integrating these technologies involves containerizing the application for easy deployment.

The Dockerfile will use pnpm to install dependencies and package the application into a Docker image.

Dive in as follows: In the project root directory, create a Dockerfile:

  • Define the image that we will use:
FROM node:20-slim AS node_base
  • Define the pnpm environmental variables:
ENV PNPM_HOME="/pnpm"
ENV PATH="$PNPM_HOME:$PATH"
  • Define the pnpm build configuration:
FROM node_base AS build
COPY . /usr/src/app
WORKDIR /usr/src/app
RUN --mount=type=cache,id=pnpm,target=/pnpm/store pnpm install --frozen-lockfile
RUN pnpm run -r build
RUN pnpm deploy --filter=main --prod /prod/main
RUN pnpm deploy --filter=shared --prod /prod/shared
  • Define build for main package:
FROM node_base AS main
COPY --from=build /prod/main /prod/main
WORKDIR /prod/main
EXPOSE 4000
CMD [ "pnpm", "start" ]
  • Define build for shared package:
FROM node_base AS shared
COPY --from=build /prod/shared /prod/shared
WORKDIR /prod/shared

Build and Run PNPM Monorepo with Docker

Now that the Docker configuration is ready for your PNPM Monorepo, it’s time to run your Workspace Monorepo with Docker.

From the project root directory, build the image for the main package:

docker build . --target main --tag main:latest
  • Similarly, build for shared package:
docker build . --target shared --tag shared:latest
  • Run the image for main:
docker run -p 4000:4000 main

The build will run and expose port 4000. Test it from your browser, and you should be able to access the below page:

Build a Monorepos with Docker; A Pnpm with Workspace Example Guide

And yes! Your workflow for setting up a Monorepo with a workspace to manage shared dependencies with Docker is working correctly.

Conclusion

Along this guide, you learned how:

  • Initialize Monorepo with Pnpm
  • Create Package Configurations
  • Define Dependencies in Monorepo
  • Create Docker Configuration
  • Build and run your Monorepo with Docker

Remember to adapt the Dockerfile and other configurations based on your specific project needs. Dive deeper and effectively use the Docker slim strategies to Reduce Docker Image Size.

Build Pnpm Workspace Monorepos with Docker Example Guide

Written By:

Joseph Chege