# syntax=docker/dockerfile:1.7 FROM node:22-bookworm-slim AS dependencies WORKDIR /app ENV NEXT_TELEMETRY_DISABLED=1 COPY package.json package-lock.json ./ RUN --mount=type=cache,target=/root/.npm npm ci --no-audit --no-fund FROM dependencies AS build WORKDIR /app COPY . . RUN npm run build && test -f .next-build/BUILD_ID FROM node:22-bookworm-slim AS production-dependencies WORKDIR /app ENV NODE_ENV=production COPY package.json package-lock.json ./ RUN --mount=type=cache,target=/root/.npm npm ci --omit=dev --no-audit --no-fund FROM node:22-bookworm-slim AS runtime RUN groupadd --system --gid 10001 organizer \ && useradd --system --uid 10001 --gid 10001 --home-dir /nonexistent --shell /usr/sbin/nologin organizer WORKDIR /app ENV NODE_ENV=production \ NEXT_TELEMETRY_DISABLED=1 \ HOSTNAME=0.0.0.0 \ PORT=3000 COPY --from=production-dependencies --chown=10001:10001 /app/node_modules/ ./node_modules/ COPY --from=build --chown=10001:10001 /app/.next-build/ ./.next-build/ COPY --from=build --chown=10001:10001 /app/public/ ./public/ COPY --from=build --chown=10001:10001 /app/container/start.mjs ./container/start.mjs COPY --from=build --chown=10001:10001 /app/package.json /app/next.config.mjs ./ RUN mkdir -p /app/.next-build/cache && chown -R 10001:10001 /app/.next-build # The tiny supervisor starts as root only long enough to read Docker secrets, # then launches Next.js as uid/gid 10001. Compose grants only SETUID/SETGID. USER 0:0 EXPOSE 3000 HEALTHCHECK --interval=30s --timeout=5s --start-period=20s --retries=3 \ CMD ["node", "-e", "fetch('http://127.0.0.1:3000/api/health').then(r=>{if(!r.ok)process.exit(1)}).catch(()=>process.exit(1))"] CMD ["node", "container/start.mjs"]