Deploying a pnpm monorepo

Hello, I’m new to Fly and I don’t understand how I should set up a pnpm monorepo on Fly, because when I follow the official article, it ends up copying all node_modules and it inflates the image size greatly.

There is an official guide from pnpm but I’m a complete noob at Docker so I’m not really sure how the final Dockerfile should look.

My fly.toml file is:

app = "stauro"
primary_region = "arn"

[env]
PORT = "3000"

[http_service]
internal_port = 3000
force_https = true
auto_stop_machines = true
auto_start_machines = true

and Dockerfile:

# syntax = docker/dockerfile:1

# Adjust NODE_VERSION as desired
ARG NODE_VERSION=20.5.1
FROM node:${NODE_VERSION}-slim as base

LABEL fly_launch_runtime="NodeJS"

RUN corepack enable

# Set production environment
ENV NODE_ENV=production

ENV PNPM_HOME="/pnpm"
ENV PATH="$PNPM_HOME:$PATH"
FROM base AS prod-deps
RUN --mount=type=cache,id=pnpm,target=/pnpm/store pnpm install --prod --frozen-lockfile

# Throw-away build stage to reduce size of final image
FROM base as build

# Install packages needed to build node modules
RUN apt-get update -qq && \
    apt-get install -y pkg-config build-essential 

COPY --link . .

# Install node modules
RUN pnpm i

# Final stage for app image
FROM base

# Copy built application
COPY --from=build /app/ /app

WORKDIR /app/packages/api

# Start the server by default, this can be overwritten at runtime
CMD [ "pnpm", "run", "start" ]

I have the following monorepo structure:

├── Dockerfile
├── fly.toml
├── package.json
├── packages
│   ├── api
│   ├── prisma
│   └── www
├── pnpm-lock.yaml
├── pnpm-workspace.yaml
└── tsconfig.json

My workspace settings are these:

packages:
  - 'packages/*'

Could I get some guidance please how to properly write a docker file so it deploys a package under app successfully? (It also depends on prisma, so it should be built as well)

I will try my best to assist you in at most 30 mins until a Fly employee can get to this.

1 Like

On it now

When you say you followed the official article, is it a Fly.io article?
Can you share the article you are referring to?

The official guide describes how to build and run what appears to be two independent apps that may share some common dependencies. I suspect that this doesn’t match your setup.

What would be most helpful to know is how you run your application today. As an example, it may be that your www and prisma apps are merely built, and the only app you are running is an api server that makes use of the database through prisma and serves bundled javascript based on the code in www.

Other configurations are possible. For example you may start two servers, where the www server talks to your api server.

Without knowing more, it is difficult to craft a dockerfile that does what you know. After all “monorepository” simply means many projects in one repository.

It would be helpful to see your package.json, both at the top level and probably also the package.json files in each directory where you start a server (I suspect that at a minimum that would include api).

I meant this tutorial

apologies for the lack of info, here are package json files for api and prisma:

// packages/api
{
  "name": "@monorepo/api",
  "private": true,
  "type": "module",
  "version": "1.0.0",
  "exports": "./dist/schema.js",
  "types": "./dist/schema.d.ts",
  "dependencies": {},
  "devDependencies": {},
  "scripts": {
    "generate": "tsc",
    "dev": "doppler run -- tsx watch src/server.ts",
    "start": "doppler run -- node ./dist/server.js",
    "postinstall": "pnpm generate"
  }
}

// packages/prisma

{
  "name": "@monorepo/prisma",
  "private": true,
  "main": "./dist/index.js",
  "types": "./dist/index.d.ts",
  "files": [
    "dist"
  ],
  "devDependencies": {
    "prisma": "5.0.0"
  },
  "dependencies": {
    "@pothos/plugin-prisma": "^3.52.0",
    "@prisma/client": "5.0.0"
  },
  "scripts": {
    "generate": "prisma generate",
    "postinstall": "pnpm generate"
  }
}

Since prisma doesn’t have a start script, it clearly isn’t an application.

I’m troubled by the fact that I don’t see any scripts being run in the build stage. I presume that you need to run something that calls the generate script in the prisma directory.


So far, I’ve only seen one start script (I’m still curious what is in web), but if you only have one application with a start script, try changing:

To

# Copy built application
COPY --from=build /app/packages/api /app

WORKDIR /app

With this change, the only app that will be in your image will be the api app. Hopefully the build process places bundled javascript from the web app and generated database access code from the prisma app into the api app; but I’m making a lot of assumptions here as I can’t see your application.

pnpm generate in prisma builds a prisma client, so it’s the same as build

the api package is missing build, my bad, the generate script should be called build.

www is unrelated, I’m not intending to deploy it. I only need to deploy api, with a dependency on prisma

So are you good to go? Or do you have more questions?

Meanwhile, some things I can do:

  • Add a js specific monorepo page to the documentation, with more JS specific advice.
  • Add a --target option to dockerfile-node so that you can regenerate your dockerfile with only a specific target.
1 Like

in the end we came up with the following Dockerfile:

FROM node:18-alpine as base

LABEL fly_launch_runtime="NodeJS"

RUN corepack enable

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

RUN wget -q -t3 'https://packages.doppler.com/public/cli/rsa.8004D9FF50437357.key' -O /etc/apk/keys/cli@doppler-8004D9FF50437357.rsa.pub && \
    echo 'https://packages.doppler.com/public/cli/alpine/any-version/main' | tee -a /etc/apk/repositories && \
    apk add doppler

WORKDIR /app

COPY . /app/

RUN npm pkg delete scripts.prepare

RUN rm -rf /app/packages/www

RUN pnpm i --prod && pnpm i -r --prod

WORKDIR /app/packages/prisma

RUN pnpm generate

WORKDIR /app/packages/api
EXPOSE 3000
CMD [ "pnpm", "start" ]

This topic was automatically closed 7 days after the last reply. New replies are no longer allowed.