Docker: full Dockerized deployment (volumes, host networking, multi-package-manager build)
- docker-compose.yml: network_mode host so 127.0.0.1 refs (.env) keep working; mounts /var/www/Gamedata + write volumes; /api/health healthcheck; mem_limit - Dockerfile: package-manager detection (pnpm/yarn/npm) + PACKAGE_MANAGER arg; runs as www-data (UID/GID 33) so gamedata is writable; .env loaded only for the build (no secrets baked in); build runs on the host network - .dockerignore: .env stays in the build context (needed for NEXT_PUBLIC_*) - README: Docker deployment section (paths, volumes, chown, migrations on host)
This commit is contained in:
1 parent
58c35a2920
commit
cb927db78d
4 files changed
+377
-411
No files matched your search
+65
-24
@@ -1,63 +1,104 @@
|
||||
# ==============================================================================
|
||||
# EpicNext-CMS — Docker image (Node 26.8.1, pnpm 11.24.0, Next.js standalone)
|
||||
# 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
|
||||
|
||||
# pnpm is required and pinned in package.json (packageManager: [email protected]).
|
||||
# Node 26 does not bundle corepack anymore, so install pnpm via npm.
|
||||
RUN npm install -g [email protected]
|
||||
# Install all three package managers so the build can pick whichever lockfile exists.
|
||||
RUN npm install -g pnpm@11.25.0 yarn
|
||||
|
||||
WORKDIR /app
|
||||
|
||||
# First copy only the manifests so dependency layers are cached.
|
||||
COPY pnpm-lock.yaml package.json pnpm-workspace.yaml .npmrc ./
|
||||
RUN pnpm install --frozen-lockfile --ignore-scripts
|
||||
# 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.
|
||||
# Copy the rest of the source (brings in whichever lockfile your project uses).
|
||||
COPY . .
|
||||
|
||||
# Build the production bundle.
|
||||
ENV NODE_ENV=production
|
||||
RUN pnpm build
|
||||
# --- 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=
|
||||
|
||||
# Copy runtime dependencies (node_modules) needed by standalone output.
|
||||
RUN pnpm prune --prod
|
||||
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
|
||||
|
||||
# Occupied base port on the host; keep PM2-style default.
|
||||
EXPOSE 3002
|
||||
|
||||
# Non-root user for security.
|
||||
RUN groupadd --system --gid 1001 nodejs \
|
||||
&& useradd --system --uid 1001 --gid nodejs nextjs
|
||||
|
||||
WORKDIR /app
|
||||
|
||||
# Storage directory for runtime uploaded media (persistent volume).
|
||||
# nitro/swf client assets (~3GB) are mounted here as volumes at runtime.
|
||||
# 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 \
|
||||
&& chown -R nextjs:nodejs /app
|
||||
/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=nextjs:nodejs /app/.next/standalone ./
|
||||
COPY --from=builder --chown=${RUN_USER} /app/.next/standalone ./
|
||||
# Copy static assets (public files served directly).
|
||||
COPY --from=builder --chown=nextjs:nodejs /app/public ./public
|
||||
COPY --from=builder --chown=${RUN_USER} /app/public ./public
|
||||
# Copy the server-side static build output.
|
||||
COPY --from=builder --chown=nextjs:nodejs /app/.next/static ./.next/static
|
||||
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 nextjs
|
||||
USER ${RUN_USER}
|
||||
|
||||
CMD ["node", "server.js"]
|
||||
Reference in new issue
Block a user