66 lines
3.3 KiB
Docker
66 lines
3.3 KiB
Docker
# syntax=docker/dockerfile:1
|
|
# Pin the runtime to the supported engine; update both stages deliberately.
|
|
FROM node:26.8.1-alpine AS migrations
|
|
WORKDIR /app
|
|
ENV NEXT_TELEMETRY_DISABLED=1
|
|
# Keep the bootstrap aligned with package.json packageManager.
|
|
# The apk cache is persisted in a BuildKit cache mount so git is not
|
|
# re-downloaded on every build.
|
|
RUN --mount=type=cache,target=/var/cache/apk \
|
|
apk add --no-cache git \
|
|
&& npm install -g [email protected]
|
|
# The pnpm store is kept in a BuildKit cache mount that persists across builds
|
|
# on the builder. This is what stops disk usage from growing unbounded: the
|
|
# downloaded dependency store is shared and reused instead of being copied into
|
|
# a fresh image layer on every build. Unlike an image layer it is also prunable
|
|
# independently, so a hard cap (see ci-deploy.sh) keeps it bounded.
|
|
ENV PNPM_HOME=/pnpm PNPM_STORE=/pnpm/store
|
|
# pnpm-workspace.yaml + .npmrc must be present too: the lockfile records the
|
|
# overrides from pnpm-workspace.yaml, and --frozen-lockfile rejects a build
|
|
# where the workspace config is absent (ERR_PNPM_LOCKFILE_CONFIG_MISMATCH).
|
|
COPY package.json pnpm-lock.yaml* pnpm-workspace.yaml* .npmrc* ./
|
|
# pnpm fetch: download all deps into the shared cache-mounted store.
|
|
RUN --mount=type=cache,target=/pnpm \
|
|
pnpm fetch --ignore-scripts
|
|
# Install offline from the cache-mounted store; the store itself stays in the
|
|
# build cache between builds.
|
|
RUN --mount=type=cache,target=/pnpm \
|
|
pnpm install --frozen-lockfile --ignore-scripts --offline
|
|
COPY . .
|
|
FROM migrations AS builder
|
|
ARG NEXT_DEPLOYMENT_ID="unknown"
|
|
ENV NEXT_DEPLOYMENT_ID="$NEXT_DEPLOYMENT_ID"
|
|
# Fixture values exist only for this build command; production secrets are runtime-only.
|
|
# Cache Next.js build output and webpack caches so rebuilds only redo the
|
|
# changed parts.
|
|
RUN --mount=type=cache,target=/app/.next/cache \
|
|
DATABASE_URL="mysql://build:[email protected]:9/build" \
|
|
HOTEL_NAME="Build fixture" APP_URL="http://localhost:3002" \
|
|
AUTH_SECRET="build-fixture-not-for-runtime-use-000000000000" \
|
|
pnpm run build
|
|
|
|
FROM node:26.8.1-alpine AS runner
|
|
ARG NEXT_DEPLOYMENT_ID="unknown"
|
|
LABEL org.opencontainers.image.revision="$NEXT_DEPLOYMENT_ID"
|
|
WORKDIR /app
|
|
ENV NODE_ENV=production \
|
|
NEXT_TELEMETRY_DISABLED=1 \
|
|
PORT=3002 \
|
|
HOSTNAME=0.0.0.0
|
|
RUN apk add --no-cache tini curl \
|
|
&& addgroup -g 33 -S nextjs && adduser -u 33 -S -G nextjs nextjs \
|
|
&& mkdir -p /app/storage /app/public/nitro-assets /app/public/swf /var/www/Gamedata \
|
|
&& chown -R 33:33 /app/storage /app/public /var/www/Gamedata
|
|
COPY --from=builder --chown=nextjs:nextjs /app/public ./public
|
|
COPY --from=builder --chown=nextjs:nextjs /app/.next/standalone ./
|
|
COPY --from=builder --chown=nextjs:nextjs /app/.next/static ./.next/static
|
|
COPY --from=builder --chown=nextjs:nextjs /app/drizzle/migrations ./drizzle/migrations
|
|
COPY --chown=nextjs:nextjs scripts/docker-start.mjs ./docker-start.mjs
|
|
USER nextjs
|
|
EXPOSE 3002
|
|
# Self-contained healthcheck so `docker run` (ci-deploy) also gets Docker-level
|
|
# health; docker-compose overrides this with its own probe if needed.
|
|
HEALTHCHECK --interval=30s --timeout=5s --start-period=30s --retries=3 \
|
|
CMD ["node", "-e", "fetch('http://127.0.0.1:'+(process.env.PORT||'3002')+'/api/health').then(r=>process.exit(r.ok?0:1)).catch(()=>process.exit(1))"]
|
|
ENTRYPOINT ["/sbin/tini", "--"]
|
|
CMD ["node", "docker-start.mjs"] |