Files
SRCmail/Dockerfile
T
Bernd RodlerandClaude Sonnet 5 fbfaf528ab fix(build): copy next/dist/lib/metadata into standalone output
output: "standalone" + next build --webpack drops the whole metadata
directory despite a plain top-level require in router-utils/filesystem.js
("../../../lib/metadata/get-metadata-route") - every packaged build
(Electron dmg and the Docker image) crashed on its very first line with
Cannot find module. Verified: a fresh dist:mac build failed to boot at
all; copying the directory by hand (same pattern already used for the
sqlcipher prebuilds and plugin bundles) fixes it, confirmed by booting
.next/standalone directly and getting a real HTTP response.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-08-07 12:50:33 +02:00

69 lines
3.4 KiB
Docker

FROM node:24-alpine AS builder
WORKDIR /app
COPY package.json package-lock.json ./
RUN npm ci
COPY . .
ENV NEXT_TELEMETRY_DISABLED=1
# Optional: serve under a subpath like /webmail. Baked into emitted asset URLs
# at build time, so it cannot be changed without rebuilding.
ARG NEXT_PUBLIC_BASE_PATH=
ENV NEXT_PUBLIC_BASE_PATH=$NEXT_PUBLIC_BASE_PATH
# Optional: avoid next-intl rewrite loops when served under a subpath.
# Baked in at build time.
ARG NEXT_PUBLIC_LOCALE_PREFIX=
ENV NEXT_PUBLIC_LOCALE_PREFIX=$NEXT_PUBLIC_LOCALE_PREFIX
# Optional: fallback UI locale (e.g. tr, de, fr) used when the visitor's
# Accept-Language header does not match any supported locale. Baked in at
# build time because next-intl wires it into client-side routing too.
ARG NEXT_PUBLIC_DEFAULT_LOCALE=
ENV NEXT_PUBLIC_DEFAULT_LOCALE=$NEXT_PUBLIC_DEFAULT_LOCALE
# Commit SHA shown in the About screen. .dockerignore excludes .git, so
# `git rev-parse` inside the build can't find it - CI must pass it in.
ARG GIT_COMMIT=unknown
ENV GIT_COMMIT=$GIT_COMMIT
# Build the first-party plugins (vnc/plugins/*) that ship with this fork -
# currently the audited S/MIME plugin, which the server installs into its
# plugin registry at startup (lib/admin/bundled-plugins.ts). Each plugin has
# its own package.json + lockfile, so this does its own npm ci.
# Runs BEFORE next build so a broken plugin fails the image build.
RUN node scripts/build-plugins.mjs
RUN npx next build --webpack
FROM node:24-alpine AS runner
LABEL org.opencontainers.image.title="Bulwark Webmail"
LABEL org.opencontainers.image.description="Modern webmail client built with Next.js and the JMAP protocol"
LABEL org.opencontainers.image.source="https://github.com/bulwarkmail/webmail"
LABEL org.opencontainers.image.url="https://github.com/bulwarkmail/webmail"
LABEL org.opencontainers.image.licenses="AGPL-3.0-only"
LABEL org.opencontainers.image.vendor="rbm.systems"
WORKDIR /app
ENV NODE_ENV=production
ENV NEXT_TELEMETRY_DISABLED=1
RUN apk upgrade --no-cache && \
npm uninstall -g npm && \
rm -rf /usr/local/lib/node_modules/npm /usr/local/bin/npx && \
addgroup --system --gid 1001 nodejs && \
adduser --system --uid 1001 nextjs
COPY --from=builder /app/public ./public
COPY --from=builder --chown=nextjs:nodejs /app/.next/standalone ./
COPY --from=builder --chown=nextjs:nodejs /app/.next/static ./.next/static
# next/dist/lib/metadata/** (get-metadata-route.js and its neighbours). A
# plain top-level require in router-utils/filesystem.js, yet Next's own
# output file tracing for `output: "standalone"` + `next build --webpack`
# drops the whole directory - the server crashes on its first line with
# "Cannot find module '../../../lib/metadata/get-metadata-route'" without
# this. Same tracing-gap class as the plugins copy below.
COPY --from=builder --chown=nextjs:nodejs /app/node_modules/next/dist/lib/metadata ./node_modules/next/dist/lib/metadata
# Staged first-party plugin bundles. Read by path at runtime, so Next's output
# file tracing does not carry them into .next/standalone - copy explicitly or
# the image boots with the S/MIME policy toggle on and no plugin installed.
COPY --from=builder --chown=nextjs:nodejs /app/vnc/plugins/build ./vnc/plugins/build
RUN mkdir -p /app/data/settings /app/data/admin /app/data/admin-state /app/data/telemetry && chown -R nextjs:nodejs /app/data
USER nextjs
EXPOSE 3000
ENV PORT=3000
ENV HOSTNAME="0.0.0.0"
CMD ["node", "server.js"]