Files
EpicNext-Cms/Dockerfile
T
openhands 10da44ee56
CI / check (push) Successful in 1m14s
CI / deploy (push) Failing after 3m10s
fix(ci): bouw met productie-.env, deploy met env+volumes en rollback
- Deploy kopieert de productie-.env van de host in de build-context:
  Next.js bakt NEXT_PUBLIC_* in en valideert DATABASE_URL/HOTEL_NAME
  (SKIP_ENV_VALIDATION is verboden voor productie, zie src/env.ts).
- Dockerfile builder installeert git (next.config.ts deploymentId).
- Deploy-container krijgt --env-file + dezelfde volumes als compose,
  stopt ook de oude compose-container (poort 3002) en rolt terug via
  compose bij een falende health check.
2026-09-04 12:22:02 +02:00

111 lines
4.6 KiB
Docker

# ==============================================================================
# EpicNext-CMS — Docker image (Node 26.8.1, multi-package-manager, Next.js standalone)
# ==============================================================================
# Supports pnpm (default), npm, and yarn. The build stage detects which package
# manager lockfile is present and uses it automatically.
# ==============================================================================
# --- Builder stage ---
FROM node:26.8.1-bookworm-slim AS builder
# git is needed by next.config.ts (git rev-parse for deploymentId) and
# ca-certificates by package registries. Build runs on host network (see
# compose: iptables is disabled), so apt has registry access here.
RUN apt-get update && apt-get install -y --no-install-recommends git ca-certificates \
&& rm -rf /var/lib/apt/lists/*
# Install all three package managers so the build can pick whichever lockfile exists.
RUN npm install -g [email protected] yarn
WORKDIR /app
# First copy only the manifests so dependency layers are cached when using pnpm.
# For npm/yarn the full context is copied below before install.
COPY package.json pnpm-workspace.yaml .npmrc ./
# Copy the rest of the source (brings in whichever lockfile your project uses).
COPY . .
# --- Detect package manager & install dependencies ---
# Priority: pnpm > yarn > npm
# Build arg lets the user force a manager; otherwise it is auto-detected.
ARG PACKAGE_MANAGER=
RUN if [ "$PACKAGE_MANAGER" = "pnpm" ] || { [ -z "$PACKAGE_MANAGER" ] && [ -f pnpm-lock.yaml ]; }; then \
echo ">> Using pnpm" && \
pnpm install --frozen-lockfile --ignore-scripts; \
elif [ "$PACKAGE_MANAGER" = "yarn" ] || { [ -z "$PACKAGE_MANAGER" ] && [ -f yarn.lock ]; }; then \
echo ">> Using yarn" && \
yarn install --frozen-lockfile --ignore-scripts; \
elif [ "$PACKAGE_MANAGER" = "npm" ] || { [ -z "$PACKAGE_MANAGER" ] && [ -f package-lock.json ]; }; then \
echo ">> Using npm" && \
npm ci --ignore-scripts; \
else \
echo "!! No lockfile found — falling back to npm install" && \
npm install --ignore-scripts; \
fi
# Build the production bundle.
# The .env file is loaded ONLY inside this RUN layer (not persisted as ENV, so no
# secrets end up in the image) — Next.js needs NEXT_PUBLIC_* + validated build-time
# values (HOTEL_NAME, DATABASE_URL, AUTH_SECRET, ...) at build time.
ENV NODE_ENV=production
RUN if [ -f .env ]; then set -a && . ./.env && set +a; fi && \
if [ "$PACKAGE_MANAGER" = "yarn" ] || { [ -z "$PACKAGE_MANAGER" ] && [ -f yarn.lock ]; }; then \
yarn build; \
elif [ "$PACKAGE_MANAGER" = "npm" ] || { [ -z "$PACKAGE_MANAGER" ] && [ -f package-lock.json ]; }; then \
npm run build; \
else \
pnpm build; \
fi
# Prune dev dependencies for the runtime image.
RUN if [ "$PACKAGE_MANAGER" = "yarn" ] || { [ -z "$PACKAGE_MANAGER" ] && [ -f yarn.lock ]; }; then \
yarn install --production --ignore-scripts && rm -rf node_modules/.cache; \
elif [ "$PACKAGE_MANAGER" = "npm" ] || { [ -z "$PACKAGE_MANAGER" ] && [ -f package-lock.json ]; }; then \
npm prune --production; \
else \
pnpm prune --prod; \
fi
# --- Runtime stage ---
FROM node:26.8.1-bookworm-slim AS runner
# The CMS writes to bind-mounted host directories (/var/www/Gamedata is owned by
# the host's www-data user, UID/GID 33). The node base image already ships a
# www-data user with UID/GID 33, which matches that ownership — so we run as
# www-data and can write to the shared gamedata directory. If your host owner
# differs, override via --build-arg RUN_USER (e.g. --build-arg RUN_USER=1000).
ARG RUN_USER=www-data
ENV NODE_ENV=production
ENV PORT=3002
ENV HOSTNAME=0.0.0.0
EXPOSE 3002
WORKDIR /app
# Storage directory for runtime uploaded media (persistent volume).
# Also create the hardcoded gamedata mount point (/var/www/Gamedata is
# bind-mounted at runtime so the CMS can read + write imported assets there).
RUN mkdir -p /app/storage \
/app/public/nitro-assets \
/app/public/swf \
/var/www/Gamedata \
&& chown -R ${RUN_USER} /app /var/www/Gamedata
# Copy standalone Next.js output (includes a minimal node_modules).
COPY --from=builder --chown=${RUN_USER} /app/.next/standalone ./
# Copy static assets (public files served directly).
COPY --from=builder --chown=${RUN_USER} /app/public ./public
# Copy the server-side static build output.
COPY --from=builder --chown=${RUN_USER} /app/.next/static ./.next/static
# Client assets + runtime uploads live outside the image (mounted volumes).
VOLUME ["/app/public/nitro-assets", "/app/public/swf", "/app/storage"]
USER ${RUN_USER}
CMD ["node", "server.js"]