# MapleBrief — production image # Multi-stage: deps -> build -> minimal runner # (Debian base: Prisma engines need glibc; Alpine breaks the schema/query engine.) # # Build args / secrets: # NEXT_PUBLIC_ADSENSE_CLIENT_ID — AdSense client id baked into the build # (empty -> ads hidden, app still works) # Runtime env: # ADMIN_API_KEY — admin/auth key (fail-closed 401 if empty) # OLLAMA_BASE / provider — LLM endpoints (or configure in admin UI) # DATABASE_URL defaults to file:/data/dev.db (volume-mounted) # Optional: bake the real AdSense client id at build time (empty -> ads hidden) ARG NEXT_PUBLIC_ADSENSE_CLIENT_ID="" FROM node:22-slim AS deps WORKDIR /app RUN apt-get update -qq && apt-get install -yq --no-install-recommends openssl ca-certificates > /dev/null && rm -rf /var/lib/apt/lists/* COPY package.json package-lock.json ./ RUN npm ci --no-audit --no-fund FROM node:22-slim AS build WORKDIR /app # inherit the global value (set via --build-arg); empty -> placeholder ads ARG NEXT_PUBLIC_ADSENSE_CLIENT_ID RUN apt-get update -qq && apt-get install -yq --no-install-recommends openssl ca-certificates > /dev/null && rm -rf /var/lib/apt/lists/* # Absolute path avoids Prisma resolving relative SQLite URLs against the # schema dir (which would double up into prisma/prisma/...). ENV DATABASE_URL="file:/app/fresh.db" COPY --from=deps /app/node_modules ./node_modules COPY . . # Verify migrations apply to a fresh DB, then stage the migrated empty DB at # the exact path the runner copies. RUN npx prisma generate \ && npx prisma migrate deploy \ && cp /app/fresh.db prisma/fresh.db \ && NEXT_PUBLIC_SITE_URL="https://news.krisforbes.ca" \ NEXT_PUBLIC_SITE_NAME="MapleBrief" \ NEXT_PUBLIC_SITE_DESCRIPTION="Canadian news briefings" \ NEXT_PUBLIC_ADSENSE_CLIENT_ID="${NEXT_PUBLIC_ADSENSE_CLIENT_ID}" \ NEXT_PUBLIC_SHOW_SIDEBAR_ADS="false" \ npm run build \ && rm -f /app/fresh.db prisma/fresh.db-journal prisma/fresh.db-shm # (prisma/fresh.db kept above for the runner stage copy) FROM node:22-slim AS runner WORKDIR /app RUN apt-get update -qq && apt-get install -yq --no-install-recommends curl > /dev/null && rm -rf /var/lib/apt/lists/* # runtime user (guard: node slim images don't always ship one) RUN id app 2>/dev/null || useradd -m -s /bin/sh app # pre-create the data dir (volume replaces it at runtime) with app ownership RUN mkdir -p /data && chown -R app:app /data USER app # Next standalone server COPY --chown=app:app --from=build /app/.next/standalone ./ # static chunks + public assets (standalone does not auto-copy these) COPY --chown=app:app --from=build /app/.next/static ./.next/static COPY --chown=app:app --from=build /app/public ./public # pre-migrated clean database snapshot (entrypoint copies it to /data on first boot) COPY --chown=app:app --from=build /app/prisma/fresh.db ./seed-dev.db COPY --chown=app:app entrypoint.sh ./entrypoint.sh RUN chmod +x entrypoint.sh ENV PORT=3000 ENV DATABASE_URL="file:/data/dev.db" EXPOSE 3000 ENTRYPOINT ["./entrypoint.sh"]