97 lines
3.8 KiB
Docker
97 lines
3.8 KiB
Docker
# syntax=docker/dockerfile:1.7
|
|
#
|
|
# Production image for MEGA INFRASTRUCTURE — designed for Coolify.
|
|
#
|
|
# Multi-stage build:
|
|
# 1. deps — fetch and cache npm dependencies
|
|
# 2. builder — compile the Next.js standalone bundle (postinstall
|
|
# automatically patches Next 16's dev-only cross-site
|
|
# guard so we can host under any domain / IP)
|
|
# 3. runner — minimal runtime image with just the standalone server
|
|
#
|
|
# The container listens on ${PORT:-3000} and binds to 0.0.0.0 so
|
|
# Coolify's Traefik proxy can reach it. The /api/health endpoint is
|
|
# used as the container healthcheck.
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# 1. deps
|
|
# ---------------------------------------------------------------------------
|
|
FROM node:20-alpine AS deps
|
|
RUN apk add --no-cache libc6-compat
|
|
WORKDIR /app
|
|
|
|
# Copy only the lockfile + manifest so this layer is cached as long
|
|
# as dependencies don't change.
|
|
COPY package.json package-lock.json* ./
|
|
# --ignore-scripts: the `postinstall` hook runs `scripts/patch-next-dev-blocker.cjs`
|
|
# which targets Next 16's dev-only cross-site guard. That module is never
|
|
# loaded in production, so the patch is unnecessary here. Skipping the
|
|
# hook also decouples the install from the rest of the workspace — the
|
|
# `scripts/` directory isn't copied until the next stage, but the hook
|
|
# would otherwise fail with `MODULE_NOT_FOUND`.
|
|
RUN npm ci --ignore-scripts --no-audit --no-fund
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# 2. builder
|
|
# ---------------------------------------------------------------------------
|
|
FROM node:20-alpine AS builder
|
|
# poppler-utils provides pdfinfo + pdftoppm, used by
|
|
# src/lib/dn-pipeline/split.ts to rasterise delivery note pages. The
|
|
# MiniMax OpenAI-compatible endpoint does not accept PDF documents, so
|
|
# pages must become images before extraction.
|
|
RUN apk add --no-cache poppler-utils
|
|
WORKDIR /app
|
|
|
|
# Pull in the prebuilt node_modules from the deps stage.
|
|
COPY --from=deps /app/node_modules ./node_modules
|
|
|
|
# Copy the rest of the source. Order matters: changing source code
|
|
# invalidates this layer, but the layer above (node_modules) is kept.
|
|
COPY . .
|
|
|
|
# Opt out of Next.js telemetry during the build.
|
|
ENV NEXT_TELEMETRY_DISABLED=1
|
|
ENV NODE_ENV=production
|
|
|
|
# Build the standalone server. This produces `.next/standalone/` which
|
|
# already contains the trimmed `node_modules` needed at runtime plus a
|
|
# ready-to-run `server.js`.
|
|
RUN npm run build
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# 3. runner
|
|
# ---------------------------------------------------------------------------
|
|
FROM node:20-alpine AS runner
|
|
# Same reason as the builder stage — the running server shells out to
|
|
# pdftoppm on every batch.
|
|
RUN apk add --no-cache poppler-utils
|
|
WORKDIR /app
|
|
|
|
ENV NODE_ENV=production
|
|
ENV NEXT_TELEMETRY_DISABLED=1
|
|
ENV PORT=3000
|
|
ENV HOSTNAME=0.0.0.0
|
|
|
|
# Create a non-root user to run the app.
|
|
RUN addgroup --system --gid 1001 nodejs \
|
|
&& adduser --system --uid 1001 nextjs
|
|
|
|
# Static assets served directly by Next.js — must be copied alongside
|
|
# the standalone output.
|
|
COPY --from=builder /app/public ./public
|
|
|
|
# The standalone output already includes a trimmed node_modules and the
|
|
# Node executable wrapper. Static chunks live in `.next/static` and are
|
|
# loaded by the standalone server at runtime — they must be copied in
|
|
# separately because `output: "standalone"` does NOT bundle them.
|
|
COPY --from=builder --chown=nextjs:nodejs /app/.next/standalone ./
|
|
COPY --from=builder --chown=nextjs:nodejs /app/.next/static ./.next/static
|
|
|
|
USER nextjs
|
|
EXPOSE 3000
|
|
|
|
# Coolify / Traefik can hit /api/health for a 200 OK liveness probe.
|
|
HEALTHCHECK --interval=30s --timeout=10s --start-period=40s --retries=3 \
|
|
CMD wget -q --spider http://127.0.0.1:3000/api/health || exit 1
|
|
|
|
CMD ["node", "server.js"]
|