Distroless + sharp
A minimal Next.js app that uses sharp (the dep next/image
server-side optimization pulls in), compiled to a single binary and
shipped on gcr.io/distroless/cc-debian12. Final image ~80MB.
This is the recipe and the e2e regression test — CI builds and boots it on every PR.
The full working source is in
examples/sharp/
in the repo.
Dockerfile
Section titled “Dockerfile”# Stage 1: Build the binaryFROM oven/bun:1.3.14 AS builder
WORKDIR /app
COPY package.json bun.lock* ./RUN bun install --no-save
COPY . .
# Install fontconfig + fonts in the builder so we can copy the# artifacts into the distroless runner. The build itself doesn't# need them, but fc-cache populates /var/cache/fontconfig with a# fresh font cache the runner can use directly.RUN apt-get update \ && apt-get install -y --no-install-recommends fontconfig fonts-dejavu-core \ && fc-cache -f \ && rm -rf /var/lib/apt/lists/*
RUN bun run build
# Stage 2: Run the compiled binaryFROM gcr.io/distroless/cc-debian12:nonroot AS runner
WORKDIR /app
ENV NODE_ENV=productionENV NEXT_TELEMETRY_DISABLED=1ENV HOSTNAME="0.0.0.0"
# Self-contained binaryCOPY --from=builder --chown=nonroot:nonroot /app/server ./
# Fontconfig + fonts + the C libs libvips dlopens for text rendering.# cc-debian12 already has libstdc++ and libgcc; these complete the# set.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"]Why each piece
Section titled “Why each piece”| Block | Why |
|---|---|
oven/bun:1.3.14 builder |
Builds the binary. Debian-based so apt-get works. |
apt-get install fontconfig fonts-dejavu-core |
libvips needs fontconfig + a font face for text rendering (watermarks, captions). Skip if you only do non-text sharp ops. |
fc-cache -f |
Populates /var/cache/fontconfig so the runner doesn’t have to rebuild the cache on first text-render. |
bun run build |
next build with the adapter → ./server. |
gcr.io/distroless/cc-debian12:nonroot |
Has glibc + libstdc++ + libgcc (the cc variant). The plain base variant lacks libstdc++ and sharp won’t load. |
COPY /etc/fonts /usr/share/fontconfig ... /var/cache/fontconfig |
All the data fontconfig expects. Drop if you don’t render text. |
COPY libfontconfig + libfreetype + libexpat + libpng16 |
Runtime libs libvips dlopens. Same caveat as fonts. |
If you don’t need text rendering
Section titled “If you don’t need text rendering”Drop the apt-get install fontconfig fonts-dejavu-core, the
fc-cache line, and all the COPY /etc/fonts-and-friends blocks.
Shrinks the image by ~5MB.
Different font face
Section titled “Different font face”Swap fonts-dejavu-core for whatever package provides the font your
watermark text uses:
fonts-liberation(Arial/Helvetica/Times metric-compatible)fonts-noto-core(Noto fonts, broad Unicode coverage)fonts-roboto
Other architectures
Section titled “Other architectures”Set NBC_TARGET=bun-linux-arm64 on the build. See
Cross-compilation.
Variants
Section titled “Variants”-
alpine + bun runtime instead. If you want to skip compilation altogether: use
oven/bun:1.3.14-alpine,apk add fontconfig ttf-dejavu,COPY .next/standalone .,CMD ["bun", "apps/web/server.js"]. Larger image (~150MB), no compile step, same resolution behavior because it’s the standard bun runtime. -
Monorepo layout with this same recipe — see Monorepo + sharp.