Use a multi-stage Docker build with Next's standalone output

Install with npm ci, ship only the standalone server and its traced
dependencies, and run as a non-root user. Shrinks the image from
1.78 GB to 438 MB.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
leanderandClaude Opus 5.5 committed 2026-10-30 22:39:14 +01:00
1 parent 703ab5a32c
commit b8e352ba22
2 files changed
+32 -5

No files matched your search

+29 -5
View File
@@ -1,11 +1,16 @@
FROM node:26-alpine
FROM node:26-alpine AS deps
WORKDIR /app
COPY package.json ./
COPY package-lock.json ./
RUN npm install
COPY package.json package-lock.json ./
RUN npm ci
FROM node:26-alpine AS builder
WORKDIR /app
COPY --from=deps /app/node_modules ./node_modules
COPY . .
# NEXT_PUBLIC_* values are inlined at build time, and pages are prerendered
@@ -17,6 +22,25 @@ ARG NEXT_PUBLIC_UMAMI_WEBSITE_ID
RUN npm run build
# Only the standalone server and its traced dependencies end up in the final
# image, not the full node_modules or the source
FROM node:26-alpine AS runner
WORKDIR /app
ENV NODE_ENV=production
ENV PORT=9000
ENV HOSTNAME=0.0.0.0
# The server writes regenerated pages to .next, so the files must be owned by
# the user it runs as
COPY --from=builder --chown=node:node /app/.next/standalone ./
COPY --from=builder --chown=node:node /app/.next/static ./.next/static
COPY --from=builder --chown=node:node /app/public ./public
USER node
EXPOSE 9000
CMD ["npm", "run", "start"]
CMD ["node", "server.js"]
+3
View File
@@ -4,6 +4,9 @@ import createNextIntlPlugin from "next-intl/plugin"
const withNextIntl = createNextIntlPlugin()
const nextConfig: NextConfig = {
// Bundles a minimal server with only the needed dependencies, used by the
// Docker image
output: "standalone",
images: {
remotePatterns: [
{