Files
fusion/Dockerfile
gsxdsm b4856e3c1e fix: separate Docker app root from project mount so -v host mounts work (#2414)
The runner stage installed the application at /project, which was also the
documented bind-mount point — mounting a host project there shadowed the CLI
and the container exited with MODULE_NOT_FOUND on packages/cli/dist/bin.js.

- Install the app under /app and run the entrypoint by absolute path.
- Reserve /workspace (empty in the image, container workdir) as the project
  mount point.
- Update docs/docker.md: mount at /workspace, and document that embedded
  Postgres/global state lives in /home/node/.fusion with a named-volume
  example so persistence actually captures it.

Verified: image builds; `docker run -v host:/workspace` boots, embedded
Postgres initializes, /api/health returns ok.

Fixes #2414

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-23 09:50:24 -07:00

115 lines
6.3 KiB
Docker

# syntax=docker/dockerfile:1
FROM node:22-slim AS builder
WORKDIR /app
RUN apt-get update \
&& apt-get install -y --no-install-recommends git build-essential python3 \
&& rm -rf /var/lib/apt/lists/*
RUN corepack enable && corepack prepare pnpm@10.33.0 --activate
COPY package.json pnpm-lock.yaml pnpm-workspace.yaml ./
# FNXC:DockerBuild 2026-07-22-13:00: The dependency-cache layer must include every
# selected workspace manifest before frozen install; otherwise pnpm omits a package's
# dependencies and the later full-workspace build can fail despite its source being copied.
COPY packages/cli/package.json ./packages/cli/package.json
COPY packages/cli-alias/package.json ./packages/cli-alias/package.json
COPY packages/core/package.json ./packages/core/package.json
COPY packages/dashboard/package.json ./packages/dashboard/package.json
COPY packages/desktop/package.json ./packages/desktop/package.json
COPY packages/droid-cli/package.json ./packages/droid-cli/package.json
COPY packages/engine/package.json ./packages/engine/package.json
COPY packages/i18n/package.json ./packages/i18n/package.json
COPY packages/mobile/package.json ./packages/mobile/package.json
COPY packages/pi-claude-cli/package.json ./packages/pi-claude-cli/package.json
COPY packages/pi-llama-cpp/package.json ./packages/pi-llama-cpp/package.json
COPY packages/plugin-sdk/package.json ./packages/plugin-sdk/package.json
COPY plugins/examples/fusion-plugin-auto-label/package.json ./plugins/examples/fusion-plugin-auto-label/package.json
COPY plugins/examples/fusion-plugin-ci-status/package.json ./plugins/examples/fusion-plugin-ci-status/package.json
COPY plugins/examples/fusion-plugin-notification/package.json ./plugins/examples/fusion-plugin-notification/package.json
COPY plugins/examples/fusion-plugin-settings-demo/package.json ./plugins/examples/fusion-plugin-settings-demo/package.json
COPY plugins/fusion-plugin-acp-runtime/package.json ./plugins/fusion-plugin-acp-runtime/package.json
COPY plugins/fusion-plugin-compound-engineering/package.json ./plugins/fusion-plugin-compound-engineering/package.json
COPY plugins/fusion-plugin-linear-import/package.json ./plugins/fusion-plugin-linear-import/package.json
COPY plugins/fusion-plugin-paperclip-runtime/package.json ./plugins/fusion-plugin-paperclip-runtime/package.json
COPY plugins/fusion-plugin-dependency-graph/package.json ./plugins/fusion-plugin-dependency-graph/package.json
COPY plugins/fusion-plugin-cli-printing-press/package.json ./plugins/fusion-plugin-cli-printing-press/package.json
COPY plugins/fusion-plugin-openclaw-runtime/package.json ./plugins/fusion-plugin-openclaw-runtime/package.json
COPY plugins/fusion-plugin-hermes-runtime/package.json ./plugins/fusion-plugin-hermes-runtime/package.json
COPY plugins/fusion-plugin-droid-runtime/package.json ./plugins/fusion-plugin-droid-runtime/package.json
COPY plugins/fusion-plugin-cursor-runtime/package.json ./plugins/fusion-plugin-cursor-runtime/package.json
COPY plugins/fusion-plugin-grok-runtime/package.json ./plugins/fusion-plugin-grok-runtime/package.json
COPY plugins/fusion-plugin-claude-runtime/package.json ./plugins/fusion-plugin-claude-runtime/package.json
COPY plugins/fusion-plugin-omp-runtime/package.json ./plugins/fusion-plugin-omp-runtime/package.json
COPY plugins/fusion-plugin-quality/package.json ./plugins/fusion-plugin-quality/package.json
COPY plugins/fusion-plugin-agent-browser/package.json ./plugins/fusion-plugin-agent-browser/package.json
COPY plugins/fusion-plugin-whatsapp-chat/package.json ./plugins/fusion-plugin-whatsapp-chat/package.json
COPY plugins/fusion-plugin-roadmap/package.json ./plugins/fusion-plugin-roadmap/package.json
COPY plugins/fusion-plugin-even-realities-glasses/package.json ./plugins/fusion-plugin-even-realities-glasses/package.json
COPY plugins/fusion-plugin-reports/package.json ./plugins/fusion-plugin-reports/package.json
RUN pnpm install --frozen-lockfile
COPY . .
RUN pnpm build
FROM node:22-slim AS runner
LABEL org.opencontainers.image.source="https://github.com/gsxdsm/fusion"
LABEL org.opencontainers.image.description="AI-orchestrated task board"
ENV NODE_ENV=production
ENV PORT=4040
RUN apt-get update \
&& apt-get install -y --no-install-recommends git \
&& rm -rf /var/lib/apt/lists/*
RUN corepack enable && corepack prepare pnpm@10.33.0 --activate
# FNXC:DockerRun 2026-07-23-00:00: The app install root must be distinct from the
# documented user-project mount point. Installing the app at /project made the
# documented `-v host:/project` bind mount shadow the CLI (MODULE_NOT_FOUND on
# packages/cli/dist/bin.js). The app now lives at /app; users mount their project
# at /workspace, which is also the runtime working directory (issue #2414).
WORKDIR /app
COPY package.json pnpm-lock.yaml pnpm-workspace.yaml ./
COPY packages/cli/package.json ./packages/cli/package.json
COPY packages/core/package.json ./packages/core/package.json
COPY packages/dashboard/package.json ./packages/dashboard/package.json
COPY packages/engine/package.json ./packages/engine/package.json
RUN pnpm install --frozen-lockfile --prod \
--filter @runfusion/fusion
COPY --from=builder /app/packages/core/dist ./packages/core/dist
COPY --from=builder /app/packages/engine/dist ./packages/engine/dist
COPY --from=builder /app/packages/dashboard/dist ./packages/dashboard/dist
COPY --from=builder /app/packages/cli/dist ./packages/cli/dist
# @runfusion/fusion references typebox at runtime via the bundled CLI.
COPY --from=builder /app/node_modules/.pnpm/typebox@*/node_modules/typebox /app/node_modules/typebox
# FNXC:DockerRun 2026-07-23-00:00: /workspace is the documented bind-mount point for
# the user's project and the container working directory, so `fn dashboard` operates
# on the mounted project. It must stay empty in the image so a bind mount never
# shadows application code.
RUN chown node:node /app \
&& mkdir -p /workspace \
&& chown node:node /workspace
USER node
WORKDIR /workspace
EXPOSE 4040
HEALTHCHECK --interval=30s --timeout=5s --start-period=20s --retries=3 \
CMD node -e "fetch('http://localhost:4040/api/health').then(r => process.exit(r.ok ? 0 : 1)).catch(() => process.exit(1))"
# FNXC:DockerRun 2026-07-23-00:00: Entrypoint uses the absolute app path so it works
# regardless of the working directory or any volume mounted at /workspace.
ENTRYPOINT ["node", "/app/packages/cli/dist/bin.js"]
CMD ["dashboard", "--host", "0.0.0.0"]