# Client Board — production image for Coolify. # # This container is the Next.js app ONLY. PocketBase is a separate, already- # hosted service (see docs/superpowers memory: "Coolify-only PocketBase") — # this Dockerfile does not run or bundle it. # # NEXT_PUBLIC_APP_URL and NEXT_PUBLIC_PB_URL are inlined into the client # JavaScript bundle at BUILD time, not read at container start. Coolify must # be told to pass them as build arguments (its "Build Variables" section), # not just as runtime environment variables — a value only set at runtime # will be missing from the browser bundle entirely. # ---- deps: install once, reused unless package*.json changes ---- FROM node:22-alpine AS deps WORKDIR /app COPY package.json package-lock.json ./ RUN npm ci # ---- builder: compile with the build-time public vars baked in ---- FROM node:22-alpine AS builder WORKDIR /app COPY --from=deps /app/node_modules ./node_modules COPY . . ARG NEXT_PUBLIC_APP_URL ARG NEXT_PUBLIC_PB_URL ENV NEXT_PUBLIC_APP_URL=$NEXT_PUBLIC_APP_URL ENV NEXT_PUBLIC_PB_URL=$NEXT_PUBLIC_PB_URL ENV NEXT_TELEMETRY_DISABLED=1 RUN npm run build # ---- runner: the small image Coolify actually deploys ---- FROM node:22-alpine AS runner WORKDIR /app ENV NODE_ENV=production ENV NEXT_TELEMETRY_DISABLED=1 # Standalone's server only binds every interface when told to — left at the # default it binds localhost-only, which is unreachable from outside the # container. ENV HOSTNAME=0.0.0.0 ENV PORT=3000 RUN addgroup --system --gid 1001 nodejs \ && adduser --system --uid 1001 nextjs COPY --from=builder /app/public ./public COPY --from=builder --chown=nextjs:nodejs /app/.next/standalone ./ COPY --from=builder --chown=nextjs:nodejs /app/.next/static ./.next/static USER nextjs EXPOSE 3000 CMD ["node", "server.js"]