Guide to Pnpm and Docker with TypeScript and Dockerfile

Posted October 13, 2023
Guide to Pnpm and Docker with TypeScript and Dockerfile

Learn to create and build Pnpm with Docker along side Dockerfile. In this guide, you’ll create a Pnpm Docker container to fire up your Pnpm projects with different Dockerfile structures.

What You Will Learn

This is a comprehensive tutorial on Pnpm and Docker with Dockerfile . It covers all you need to know and get your Pnpm project running with Docker. You will learn the following:

  • Creating a simple basic Pnpm TypeScript app and use Pnpm Docker file to run it.
  • Create a Docker multistage Dockerfile for creating production Pnpm Docker containers
  • Use template frameworks such as Nest.js and Next.js to bootstrap the Pnpm app and run them with Docker

Are you ready? Let’s now dive and take your Pnpm Docker app to the next level.

Prerequisites to Running Pnpm with Docker

All Code samples used on this exmaple are on this GitHub repo

Using Pnpm and Docker: A TypeScript Example

Pnpm has support for TypeScript. I choose to use TypeScript, Pnpm and Docker so you can understand Pnpm Docker integration in detail.

First, you need a working TypeScript app. If you don’t have one, follow these steps and get ready.

  • Get your Pnpm manager installed using npm or Yarn
npm install -g pnpm
yarn global add pnpm
  • Create a project directory:
mkdir typescript_pnpm
cd typescript_pnpm
  • Now use Pnpm to initialize your app:
pnpm init
  • Install TypeScript for both your project and globally:
npm install -g typescript   
pnpm add --save-dev typescript
  • Use TypeScript to generate your tsconfig.json file:
npx tsc --init
  • Get your packages ready using Pnpm manager:
pnpm add express ts-node @types/express @types/node
  • Create your project files:
mkdir src
touch src/app.ts
  • Create a simple TypeScript app:
// src/app.ts
import express from 'express';

const app = express();
const port = process.env.PORT || 3000;

app.get('/', (req, res) => {
  res.send('Hello, TypeScript with Express!');
});

app.listen(port, () => {
  console.log(`Server is running on port ${port}`);
});
  • Update the package.json file to add build and start scripts.
"scripts": {
    "start": "node dist/app.js",
    "build": "tsc",
    "dev": "ts-node src/app.ts"
},
  • Make sure your tsconfig.ts file can access and build your app:
"rootDir": "./src",
"outDir": "./dist",

Your app is ready, and the run command should be:

# Compile typescript code to JavaScript
pnpm run build
# development (with automatic code reloading)
pnpm run dev
# production
pnpm start

This app runs using Pnpm as the packager manager; the question is, how do we get everything working as a Pnpm Docker container? Let’s find out.

Creating TypeScript Pnpm Dockerfile

If you have used Docker before, Dockerfile gives instructions on how docker should run your application as a container. This is not different to what you need for a Pnpm Docker container running.

On your project directory, create a Dockerfile and add the following commands: I have added comments to make it easier for you to understand:

# Use an official Node.js, and it should be version 16 and above
FROM node:20-alpine
# Set the working directory in the container
WORKDIR /app
# Copy package.json and pnpm-lock.yaml
COPY pnpm-lock.yaml package.json ./
# Install app dependencies using PNPM
RUN npm install -g pnpm
# Install dependencies
RUN pnpm i 
# Copy the application code 
COPY . .
# Build the TypeScript code
RUN pnpm run build
# Expose the app
EXPOSE 3000
# Start the application
CMD ["pnpm", "start"]

Basically, this Dockerfile will complete all the previous steps that you would have done manually:

  • You have Node.js runtime installed:
  • Create your working directory
  • Copy the package.json and pnpm-lock.yaml files that contain all your dependencies and their related version.
  • To use Pnpm, you must install it within Docker using RUN npm install -g pnpm.
  • When it is installed, you can now run pnpm i to install dependencies, then copy your Pnpm TypeScript code to Docker
  • Because TypeScript must be complied with JavaScript, RUN pnpm run build will handle that perfectly, expose your application and start it using CMD ["pnpm", "start"] as you would on your terminal.

Your TypeScript Pnpm Dockerfile is ready. Now, you need to run the following command to build your Pnpm Docker image:

Ensure you run the command inside your project directory pointing to the Dockerfile root path.

docker build -t pnpm-app .

Guide to Pnpm and Docker with TypeScript and Dockerfile

This should build and Create your Pnpm image on Docker, so confirm those results:

Guide to Pnpm and Docker with TypeScript and Dockerfile

To test your Pnpm Docker container, run the following command to run a Pnpm Docker container:

docker run -d -p 3000:3000 test-app    

This should successfully open your application and serve on http://localhost:3000/

GitHub Code Repo

Creating Pnpm with Docker Multistage Builds

The above example is the simplest Pnpm Docker setup you can create. Using the same application, let’s dive deeper and create a multistage image that you can use to run the same application in production.

Multistage builds create different images within one Dockerfile. However, Doker will complete the artifacts of those images to create a final optimized image that is slim and production-ready.

If you want to learn more about Multistage Builds and how it Slims a Docker Image, check out this guide.

In the example, the following will be the perfect Dockerfile example you can create to run your Pnpm Docker Multistage Builds:

FROM node:20-slim AS base
# Enable Corepack
RUN corepack enable
COPY . /app
WORKDIR /app

# Create a new image for production dependencies
FROM base as prod-deps
# Install production dependencies
# use a cache if available
RUN --mount=type=cache,id=pnpm,target=/pnpm/store pnpm install --prod --frozen-lockfile

# Create build image for building the TypeScript code
FROM base AS build
# Install build dependencies
RUN --mount=type=cache,id=pnpm,target=/pnpm/store pnpm install --frozen-lockfile
# Build the TypeScript
RUN pnpm run build

# Create an image base
FROM base
# Copy production dependencies from the prod-deps stage
COPY --from=prod-deps /app/node_modules /app/node_modules
# Copy the compiled TypeScript code from the build stage
COPY --from=build /app/dist /app/dist
# Expose incoming connections
EXPOSE 5000
# Start PNPM script
CMD [ "pnpm", "start" ]

Let’s digest what’s happening here:

  • You have all the basic setup as the previous example, until you introduce another FROM statement. You basically run the as follows:
  • Stage 1: base image using the node.js that copies your project code and sets the working directory.
  • Stage 2: prod-deps image - Install production dependencies using and if you have a cache, it improves build performance.
  • Stage 3: build stage - build TypeScript code and prepare it for production.
  • Stage 4: final image - creates the final image for production. Use the prod-deps and build stages. Here, your Pnpm image is ready, and you can expose it and use the Pnpm start script.

In this case, Docker will only use the final image to compile Pnpm, making your app small and easy to deploy.

Now you can build your Pnpm Docker image:

docker build -t pnpm-multistage .

To test your Pnpm Docker container, run the following command to run a Pnpm Docker container:

docker run -d -p 5000:5000 pnpm-multistage   

This should successfully open your Pnpm Docker application and serve on http://localhost:5000/

GitHub repo

Using CLI Template Frameworks to Build Pnpm Docker

If you have used templated frameworks using CLI, such as Next.js, Nest.js, React and Nuxt, you will find that creating a Pnpm project is out of the box. This means you don’t have to go the long way as we did in the above steps.

So, in this section, you will learn how to take such applications and run Pnpm with Docker. In this example, I will use Nest.js. And you can use your framework of choice as well.

To use Nest.js, install its CLI:

npm install -g @nestjs/cli

Bootstrap your Nest.js app using the CLI as follows:

nest new pnpm_example  

Be keen here as you must select to use Pnpm as your package manager:

Guide to Pnpm and Docker with TypeScript and Dockerfile

This step is similar to using other framework CLIs such as create-react-app, create-next-app and create-nuxt-app. So, you are using, let’s say, Next.js, you will create your Pnpm project specifying that in your command, i.e, pnpm create next-app my-nextjs-app:

To verify your app was created using Pnpm and not NPM or Yarn, you should have a pnpm-lock.yaml instead.

Now let’s create a Pnpm Docker container for the Next.js and Nest.js Examples so you can get the difference in details.

Change the directory to your pnpm_example folder:

cd pnpm_example

Guide to Pnpm and Docker with TypeScript and Dockerfile

Create a Dockerfile inside the pnpm_example root as follows:

FROM node:20
# Install pnpm globally
RUN npm i -g pnpm
WORKDIR /app
# Copy pnpm-lock.yaml and package.json
COPY pnpm-lock.yaml package.json ./
# Install project dependencies using pnpm
RUN pnpm i
# Copy source code
COPY . .
EXPOSE 3000
# Command to run the container
CMD [ "pnpm", "start" ]

And you can see that your Pnpm Dockerfile is simple to understand, just like the previous step.

You can still go ahead and run the above just using the multistage build as follows:

FROM node:20
# Install pnpm globally
RUN npm i -g pnpm

#Build stage on top of 'base'
FROM base AS dependencies
WORKDIR /app
# Copy package.json and pnpm-lock.yaml
COPY package.json pnpm-lock.yaml ./
# Install dependencies
RUN pnpm install

# Create a build stage based on the 'base' stage
FROM base AS build
WORKDIR /app
# Copy code
COPY . .
# Copy dependencies stage
COPY --from=dependencies /app/node_modules ./node_modules
# Build the application using pnpm
RUN pnpm build
# Prune development dependencies
RUN pnpm prune --prod

# Deployment stage from the base
FROM base AS deploy
WORKDIR /app
# Copy the built application
COPY --from=build /app/dist/ ./dist/
# Copy the production dependencies
COPY --from=build /app/node_modules ./node_modules
# Run the application
CMD [ "node", "dist/main.js" ]

GitHub Code repo

Conclusion

In this guide you’ve learned the following:

  • How to Create a simple basic Pnpm TypeScript app and use Pnpm Docker to run it.
  • Create a Docker multiBuild Dockerfile for creating production Pnpm Docker containers.
  • Use a template framework such as nest.js to bootstrap the Pnpm app and run it with Docker.
Guide to Pnpm and Docker with TypeScript and Dockerfile

Written By:

Joseph Chege