# ============================================================================= # 0pt web console -- Dockerfile for Coolify (SIAX Cloud) # ============================================================================= # This repo's deployable service is the Next.js 16 app at the repo root # (package.json `start` == `next start`, liveness at GET /api/health). Unlike # the tsx backend services elsewhere in the fleet (aud0/n0d/api0), a Next app # needs a real build step, so this is a multi-stage build: install+build, then # a slim runtime that runs the built output as the non-root `node` user. # # node:24-alpine matches the tag already proven in ../aud0 and ../api0. `wget` # is added to the runtime stage so Coolify's container healthcheck has a binary # to hit /api/health (bare node:*-alpine ships neither curl nor wget). # # next.config.mjs does NOT set `output: "standalone"` (that would be a source # change, out of Wave-1 scope), so the runtime stage carries node_modules + the # .next build output and runs `next start` directly. # ============================================================================= # ---- build stage ------------------------------------------------------------ FROM node:24-alpine AS builder WORKDIR /app ENV NEXT_TELEMETRY_DISABLED=1 # Enable corepack (pnpm@9 pinned below to match pnpm-lock.yaml lockfileVersion 9.0). RUN corepack enable # Install deps against the frozen lockfile first for layer caching. # --ignore-workspace: the repo's pnpm-workspace.yaml is a v0-export artifact that # carries only an `allowBuilds:` block and NO `packages:` field, which makes pnpm # abort with "packages field missing or empty". There are no real workspace # members (only the root package.json), so ignoring the workspace file is # semantically a no-op and lets the frozen install resolve the root deps cleanly. COPY package.json pnpm-lock.yaml .npmrc ./ RUN corepack prepare pnpm@9 --activate && pnpm install --frozen-lockfile --ignore-workspace # Build the app. The lib/* clients (db, stripe, ...) are lazy + env-guarded, so # `next build` succeeds without any runtime secrets present. # --ignore-workspace again here: `COPY . .` reintroduces the v0 pnpm-workspace.yaml, # and `pnpm run` re-parses it (same "packages field missing" abort) unless ignored. COPY . . RUN pnpm --ignore-workspace run build # ---- runtime stage ---------------------------------------------------------- FROM node:24-alpine AS runtime WORKDIR /app ENV NODE_ENV=production ENV NEXT_TELEMETRY_DISABLED=1 ENV HOSTNAME=0.0.0.0 ENV PORT=3000 RUN apk add --no-cache wget # Built artifacts + resolved dependencies from the build stage. .next is chowned # so `next start` can write its runtime cache as the non-root user. COPY --from=builder /app/node_modules ./node_modules COPY --from=builder --chown=node:node /app/.next ./.next COPY --from=builder /app/public ./public COPY --from=builder /app/package.json ./package.json COPY --from=builder /app/next.config.mjs ./next.config.mjs USER node EXPOSE 3000 HEALTHCHECK --interval=30s --timeout=5s --start-period=30s --retries=3 \ CMD wget -qO- http://127.0.0.1:3000/api/health >/dev/null 2>&1 || exit 1 # Invoke the next binary directly (pnpm/corepack are not set up in this stage). CMD ["node_modules/.bin/next", "start"]