Add a conditional cleanup step to remove the public directory if it does not exist or contains no files. This reduces the final docker image size since Next.js standalone builds automatically inline public assets when they are present, making the copied public directory often redundant.
88 lines
3.6 KiB
Docker
88 lines
3.6 KiB
Docker
# ─────────────────────────────────────────────────────────────────
|
|
# Workflow PocketBase Frontend — Dockerfile
|
|
#
|
|
# Multi-stage build for a small production image:
|
|
# - deps: install everything
|
|
# - builder: compile the Next.js app (writes standalone output)
|
|
# - runner: copy the standalone build + public assets + .next/static
|
|
# onto the alpine image
|
|
#
|
|
# Image runs as non-root user `nextjs` on port 3000.
|
|
#
|
|
# Build context: project root (where package.json lives).
|
|
# Example:
|
|
# docker build -t workflow-frontend .
|
|
# docker run --rm -p 3000:3000 \
|
|
# -e NEXT_PUBLIC_BASE_URL=https://workflow.example.co.za \
|
|
# -e PB_URL=https://pb.agent.apphub.co.za \
|
|
# -e PB_TOKEN=... \
|
|
# workflow-frontend
|
|
# ─────────────────────────────────────────────────────────────────
|
|
|
|
# ─── 1. Deps ─────────────────────────────────────────────────────
|
|
FROM node:20-alpine AS deps
|
|
WORKDIR /app
|
|
COPY package.json package-lock.json* ./
|
|
RUN \
|
|
if [ -f package-lock.json ]; then npm ci; \
|
|
else npm install; \
|
|
fi
|
|
|
|
# ─── 2. Builder ──────────────────────────────────────────────────
|
|
FROM node:20-alpine AS builder
|
|
WORKDIR /app
|
|
COPY --from=deps /app/node_modules ./node_modules
|
|
COPY . .
|
|
|
|
# Required at build-time so Next.js bakes the public URL into the
|
|
# client bundle. PB_TOKEN is NOT required here — it's only read
|
|
# server-side at runtime.
|
|
ARG NEXT_PUBLIC_BASE_URL
|
|
ENV NEXT_PUBLIC_BASE_URL=${NEXT_PUBLIC_BASE_URL}
|
|
|
|
# Disable telemetry; speeds up build; we don't need it.
|
|
ENV NEXT_TELEMETRY_DISABLED=1
|
|
|
|
RUN npm run build
|
|
|
|
# ─── 3. Runner ───────────────────────────────────────────────────
|
|
FROM node:20-alpine AS runner
|
|
WORKDIR /app
|
|
|
|
# wget is needed by the HEALTHCHECK below (not in node:20-alpine
|
|
# by default — busybox-extra ships it).
|
|
RUN apk add --no-cache wget
|
|
|
|
ENV NODE_ENV=production
|
|
ENV NEXT_TELEMETRY_DISABLED=1
|
|
ENV PORT=3000
|
|
ENV HOSTNAME=0.0.0.0
|
|
|
|
# Non-root user
|
|
RUN addgroup --system --gid 1001 nodejs && \
|
|
adduser --system --uid 1001 nextjs
|
|
|
|
# Copy the standalone build (Next.js output tracing) + static assets.
|
|
# `public/` is optional (PWA manifest, favicons, robots.txt). Next
|
|
# inlines any source `public/` into the standalone build, so we only
|
|
# need to copy it if it actually exists. Projects without a public/
|
|
# dir (e.g. this one pre-PWA) no-op cleanly.
|
|
COPY --from=builder --chown=nextjs:nodejs /app/.next/standalone ./
|
|
COPY --from=builder --chown=nextjs:nodejs /app/.next/static ./.next/static
|
|
RUN if [ -d ./public ] && [ -n "$(ls -A ./public 2>/dev/null)" ]; then \
|
|
echo "public/ assets found in standalone build, keeping"; \
|
|
else \
|
|
rm -rf ./public 2>/dev/null || true; \
|
|
fi
|
|
|
|
USER nextjs
|
|
EXPOSE 3000
|
|
|
|
# Health check — Coolify / Docker can probe /api/pb/records/list and
|
|
# 401 / 500 is fine here (it means the app booted; PB may be
|
|
# unreachable but the proxy is running). A connection refused means
|
|
# the Next.js server hasn't bound yet.
|
|
HEALTHCHECK --interval=30s --timeout=5s --start-period=20s --retries=3 \
|
|
CMD wget -qO- http://127.0.0.1:3000/api/pb/records/list?collection=ysterbaard_directors\&perPage=1 >/dev/null || exit 1
|
|
|
|
CMD ["node", "server.js"]
|