Vercel Workflow DevKit: Fixing Module Not Found in Docker

Getting Cannot find module '@workflow-worlds/redis' when running Workflow DevKit on Nitro in Docker? Adjusting WORKDIR fixes it; full Dockerfile included.

CChia1104
  • Notes
  • 2 minutes read

Module Notfound Issue

Recently, while using Vercel’s Workflow DevKit to handle the task queue architecture for generating embeddings, I ran into a problem with the Redis PUB/SUB module @workflow-worlds/redis.

When deploying, I encountered a “module not found” error:

Cannot find module '@workflow-worlds/redis'

Since I was using a Monorepo setup and my backend was served through Nitro, my project structure looked like this:

package.json

After Nitro finished building, it produced a .output folder along with a transpiled node_modules structure:

index.mjs
package.json
package.json
package.json
package.json

At that time, my Dockerfile looked like this:

ARG NODE_VERSION=24

FROM node:${NODE_VERSION}-alpine AS base

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

WORKDIR /app

RUN apk update && \
    apk add --no-cache \
    python3 \
    build-base \
    gcc \
    libc6-compat && \
    npm install -g corepack@latest && \
    corepack enable pnpm && \
    pnpm add -g turbo && \
    ln -sf /usr/bin/python3 /usr/bin/python

FROM base AS pre-builder

COPY . .

RUN turbo prune --scope=service --docker

FROM base AS builder

COPY --from=pre-builder /app/out/json/ .
COPY --from=pre-builder /app/out/pnpm-lock.yaml ./pnpm-lock.yaml
COPY --from=pre-builder /app/out/full/ .
COPY turbo.json turbo.json

ARG TURBO_TEAM \
    TURBO_TOKEN

ENV TURBO_TEAM=$TURBO_TEAM \
    TURBO_TOKEN=$TURBO_TOKEN

RUN pnpm i

RUN pnpm turbo run build --filter=service...

FROM base AS runner

COPY --from=builder /app/apps/service/.output/server ./apps/service

ENV NODE_ENV=production \
    PORT=8080

EXPOSE 8080

CMD [ "node", "app/apps/service/index.mjs" ]

All layers share the same working directory (/app). Since Workflow dynamically loads the target module via the WORKFLOW_TARGET_WORLD environment variable within @workflow/core/runtime, it uses the following logic:

const require = createRequire(join(process.cwd(), 'index.js'));

Source: @workflow/core/runtime

This means the module resolution depends on the current working directory from which Node is executed. Therefore, the entrypoint that starts the server must be at the same directory level as the node_modules folder containing @workflow-worlds/redis.

The Fix

Adjust the WORKDIR setting:

ARG NODE_VERSION=24

FROM node:${NODE_VERSION}-alpine AS base

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

WORKDIR /app

RUN apk update && \
    apk add --no-cache \
    python3 \
    build-base \
    gcc \
    libc6-compat && \
    npm install -g corepack@latest && \
    corepack enable pnpm && \
    pnpm add -g turbo && \
    ln -sf /usr/bin/python3 /usr/bin/python

FROM base AS pre-builder

COPY . .

RUN turbo prune --scope=service --docker

FROM base AS builder

COPY --from=pre-builder /app/out/json/ .
COPY --from=pre-builder /app/out/pnpm-lock.yaml ./pnpm-lock.yaml
COPY --from=pre-builder /app/out/full/ .
COPY turbo.json turbo.json

ARG TURBO_TEAM \
    TURBO_TOKEN

ENV TURBO_TEAM=$TURBO_TEAM \
    TURBO_TOKEN=$TURBO_TOKEN

RUN pnpm i

RUN pnpm turbo run build --filter=service...

FROM base AS runner

COPY --from=builder /app/apps/service/.output/server ./apps/service

ENV NODE_ENV=production \
    PORT=8080

WORKDIR /app/apps/service

EXPOSE 8080

CMD [ "node", "index.mjs" ]
Last updated

Written by: Chia1104 CC BY-NC-SA 4.0