# Optimal Dockerfile for Node.js with pnpm (https://depot.dev/docs/container-builds/optimal-dockerfiles/node-pnpm-dockerfile)

Below is an example `Dockerfile` that we recommend at Depot for building Docker images for Node applications that use `pnpm` as their package manager.

```dockerfile
# syntax=docker/dockerfile:1

FROM node:lts AS build

RUN corepack enable

ENV PNPM_HOME="/pnpm"
ENV PATH="$PNPM_HOME:$PATH"

WORKDIR /app

COPY pnpm-lock.yaml ./

RUN --mount=type=cache,target=/pnpm/store \
    pnpm fetch

COPY package.json ./

RUN --mount=type=cache,target=/pnpm/store \
    pnpm install --frozen-lockfile --prod --offline

COPY . .

RUN pnpm build

FROM node:lts AS runtime

RUN groupadd -g 1001 appgroup && \
    useradd -u 1001 -g appgroup -m -d /app -s /bin/false appuser

WORKDIR /app

COPY --from=build --chown=appuser:appgroup /app ./

ENV NODE_ENV=production \
    NODE_OPTIONS="--enable-source-maps"

USER appuser

ENTRYPOINT ["node", "server.js"]
```

## Explanation of the Dockerfile

This Dockerfile uses an optimized multi-stage build approach that leverages pnpm's features for efficient dependency management and caching. We use Node.js LTS and implement security optimizations.

At a high level, here are the things we're optimizing in our Docker build for a Node.js application with pnpm:

* Multi-stage builds via multiple `FROM` statements
* pnpm cache mounts for dependency caching
* Offline installation for improved reliability
* Security optimizations with non-root users

### Stage 1: `FROM node:lts AS build`

```dockerfile
FROM node:lts AS build

RUN corepack enable

ENV PNPM_HOME="/pnpm"
ENV PATH="$PNPM_HOME:$PATH"

WORKDIR /app
```

We start with the Node.js LTS image as our build stage base. We enable [`corepack`](https://nodejs.org/api/corepack.html) to use pnpm without manual installation, and we set up the proper environment variables for pnpm's home directory.

#### Production dependency installation

```dockerfile
COPY pnpm-lock.yaml ./

RUN --mount=type=cache,target=/pnpm/store \
    pnpm fetch --frozen-lockfile

COPY package.json ./

RUN --mount=type=cache,target=/pnpm/store \
    pnpm install --frozen-lockfile --prod --offline
```

We copy the lockfile first to leverage Docker's layer caching. The installation process uses two optimized commands:

1. `pnpm fetch --frozen-lockfile` is a [pnpm feature](https://pnpm.io/cli/fetch) that fetches packages from the lockfile into the pnpm store without installing them. This optimizes the Docker layer cache.

2. `pnpm install --frozen-lockfile --prod --offline` installs only production dependencies using the cached packages from the previous step. The `--offline` flag ensures we use only cached packages.

#### Building the application

```dockerfile
COPY . .

RUN pnpm build
```

After copying the source code, we build the application using pnpm.

### Stage 2: `FROM node:lts AS runtime`

```dockerfile
FROM node:lts AS runtime

RUN groupadd -g 1001 appgroup && \
    useradd -u 1001 -g appgroup -m -d /app -s /bin/false appuser

WORKDIR /app

COPY --from=build --chown=appuser:appgroup /app ./

ENV NODE_ENV=production \
    NODE_OPTIONS="--enable-source-maps"

USER appuser

ENTRYPOINT ["node", "server.js"]
```

The runtime stage uses the Node.js LTS image and creates a non-root user for security. We copy the entire built application from the build stage, setting appropriate ownership.

## Understanding BuildKit Cache Mounts

Cache mounts in this Dockerfile speed up builds by persisting the package manager cache. This means that even when a layer needs to be rebuilt, your package manager only fetches what's new or updated. This Dockerfile uses the following cache mount syntax:

```dockerfile
RUN --mount=type=cache,target=/pnpm/store \
    pnpm fetch --frozen-lockfile
```

### Cache Mount Parameters Explained

* **`type=cache`**: Specifies this is a cache mount. The cache persists across builds and is managed by BuildKit (and Depot's distributed cache system).
* **`target=/pnpm/store`**: The mount point inside the container where pnpm's store is located. Unlike npm, pnpm uses a content-addressable store that can be shared efficiently across projects.

For more information regarding pnpm cache mounts, please visit the official [pnpm documentation](https://pnpm.io/configuring).

## For AI Agents

The full site index is at [llms.txt](https://depot.dev/llms.txt). Append `.md` to any documentation, blog, changelog, or customer URL to fetch its markdown source directly.