Monorepo + sharp
A bun-workspaces monorepo with one Next.js app (apps/web) and one
shared package (packages/shared). The web app uses sharp + the
shared package and is compiled to a single binary on
gcr.io/distroless/cc-debian12.
This recipe doubles as the e2e regression test for two things at once:
- Nested monorepo layout — the adapter’s assembled output tree
preserves the app path (
apps/web/...relative to the repo root). next-bun-compile handles the nesting automatically. - Workspace package resolution —
@example/sharedis a workspace-versioned dep. It survives the build trace round-trip into the binary.
Full source in
examples/monorepo/.
Project layout
Section titled “Project layout”monorepo/├── package.json # workspaces: ["apps/*", "packages/*"]├── apps/│ └── web/│ ├── app/api/resize/route.ts # uses sharp + @example/shared│ ├── next.config.ts│ └── package.json├── packages/│ └── shared/│ ├── index.ts # exports pickColor(seed)│ └── package.json└── DockerfileDockerfile
Section titled “Dockerfile”FROM oven/bun:1.3.14 AS builder
WORKDIR /app
# Workspace setup: root package.json + all workspace package.jsons# need to be present before `bun install` so the workspace graph# resolves. Copy them all first, then install once.COPY package.json bun.lock* ./COPY apps/web/package.json ./apps/web/COPY packages/shared/package.json ./packages/shared/RUN bun install --no-save
# Now copy the source codeCOPY . .
# Fontconfig + fonts for sharp/libvips text rendering. Skip if your# sharp usage is resize/format-only.RUN apt-get update \ && apt-get install -y --no-install-recommends fontconfig fonts-dejavu-core \ && fc-cache -f \ && rm -rf /var/lib/apt/lists/*
# Build the web app — produces apps/web/server (single binary)RUN cd apps/web && bun run build
# Runner: distroless with libstdc++ + fontconfigFROM gcr.io/distroless/cc-debian12:nonroot AS runner
WORKDIR /app
ENV NODE_ENV=productionENV NEXT_TELEMETRY_DISABLED=1ENV HOSTNAME="0.0.0.0"
COPY --from=builder --chown=nonroot:nonroot /app/apps/web/server ./
COPY --from=builder /etc/fonts /etc/fontsCOPY --from=builder /usr/share/fontconfig /usr/share/fontconfigCOPY --from=builder /usr/share/fonts /usr/share/fontsCOPY --from=builder /var/cache/fontconfig /var/cache/fontconfigCOPY --from=builder /usr/lib/x86_64-linux-gnu/libfontconfig.so* /usr/lib/x86_64-linux-gnu/COPY --from=builder /usr/lib/x86_64-linux-gnu/libfreetype.so* /usr/lib/x86_64-linux-gnu/COPY --from=builder /usr/lib/x86_64-linux-gnu/libexpat.so* /usr/lib/x86_64-linux-gnu/COPY --from=builder /usr/lib/x86_64-linux-gnu/libpng16.so* /usr/lib/x86_64-linux-gnu/
EXPOSE 3000CMD ["./server"]Build it
Section titled “Build it”docker build -t next-bun-compile-monorepo .docker run --rm -p 3000:3000 next-bun-compile-monorepoAdapting to your monorepo
Section titled “Adapting to your monorepo”- Turborepo / Nx. Same recipe — next-bun-compile cares about the
traced output layout, not the workspace tool. If you use
turbo prune --dockerin CI to slim the Docker context, the pruned tree still works. - pnpm workspaces. Works. The
.pnpm/hoisted store is handled by the same code as the.bun/store. - Many apps in one repo. next-bun-compile scopes to whatever app’s
next buildyou run; it doesn’t try to detect siblings. Build each app independently. - Workspace dep changed but binary stale?
next builddoesn’t rebuild on workspace-package changes by default. Add the workspace package totranspilePackages(or rely on file watching during development) if you hit this.