Fixing “Module Not Found” in Vercel Workflow DevKit with Nitro and Docker

Encountered Cannot find module '@workflow-worlds/redis' when deploying a Vercel Workflow DevKit app using Nitro and Docker? This guide explains the root cause and shows how adjusting the WORKDIR fixes the issue. Includes full Dockerfile example and detailed setup instructions.

CChia1104
Notes2 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" ]

Written by: Chia1104 CC BY-NC-SA 4.0

Chia1104
©
Chia1104