# syntax=docker/dockerfile:1 # # Dungeons & Ground — production image. # # Build: docker build -t dungeons-ground . # Run: docker run --env-file .env -p 3000:3000 dungeons-ground # # Required env vars at runtime (see .env.example): # NUXT_PUBLIC_SUPABASE_URL, SUPABASE_URL, NUXT_PUBLIC_SUPABASE_ANON_KEY, # SUPABASE_SERVICE_ROLE_KEY, plus the AI provider key (OPENROUTER_API_KEY # or DEEPSEEK_API_KEY). REDIS_URL is optional. # The Supabase schema is NOT created by the container; install # supabase/bootstrap.sql (or run `pnpm db:ensure` with SUPABASE_DB_URL set) # in your Supabase project before starting. FROM node:18.20.8-slim AS base ENV PNPM_HOME=/pnpm ENV PATH=$PNPM_HOME:$PATH RUN corepack enable && corepack prepare pnpm@10.15.0 --activate WORKDIR /app # ---------- Dependencies ---------- FROM base AS deps COPY package.json pnpm-lock.yaml pnpm-workspace.yaml ./ COPY apps/web/package.json apps/web/package.json COPY apps/worker/package.json apps/worker/package.json COPY packages/shared/package.json packages/shared/package.json COPY packages/game-engine/package.json packages/game-engine/package.json RUN pnpm install --frozen-lockfile # ---------- Build ---------- FROM deps AS build COPY . . # The web build explicitly loads ../../.env (--dotenv); an empty file keeps # the build hermetic — all real values come from container env at runtime. RUN touch .env && pnpm --filter @dng/web build # ---------- Runtime ---------- FROM base AS runtime ENV NODE_ENV=production ENV HOST=0.0.0.0 ENV PORT=3000 WORKDIR /app COPY package.json pnpm-lock.yaml pnpm-workspace.yaml ./ COPY apps/web/package.json apps/web/package.json COPY apps/worker/package.json apps/worker/package.json COPY packages/shared/package.json packages/shared/package.json COPY packages/game-engine/package.json packages/game-engine/package.json COPY --from=deps /app/node_modules node_modules COPY --from=build /app/apps/web/.output apps/web/.output COPY --from=build /app/apps/worker apps/worker COPY --from=build /app/packages/shared packages/shared COPY --from=build /app/packages/game-engine packages/game-engine EXPOSE 3000 # Coolify (and other hosts) override PORT at runtime; probe that port, not a # hardcoded one. HEALTHCHECK --interval=30s --timeout=5s --start-period=30s --retries=3 \ CMD node -e "fetch('http://127.0.0.1:' + (process.env.PORT || 3000) + '/').then(r=>process.exit(r.ok?0:1)).catch(()=>process.exit(1))" # The Nuxt web server reads its config through Nuxt runtimeConfig, which is # baked at build time and overridden at runtime only by NUXT_-prefixed env # vars; the worker reads plain names (SUPABASE_URL, OPENROUTER_API_KEY, ...) # directly. The entrypoint maps the plain .env.example names onto the NUXT_ # runtime overrides so `docker run --env-file .env` works for both processes. # The web server runs in the foreground (PID 1); the AI worker runs alongside # it in the background. Stop the container with `docker stop` — both die with # it. CMD ["sh", "-c", "export NUXT_SUPABASE_URL=\"${NUXT_SUPABASE_URL:-${SUPABASE_URL:-}}\" NUXT_SUPABASE_SERVICE_ROLE_KEY=\"${NUXT_SUPABASE_SERVICE_ROLE_KEY:-${SUPABASE_SERVICE_ROLE_KEY:-}}\" NUXT_AI_PROVIDER=\"${NUXT_AI_PROVIDER:-${AI_PROVIDER:-}}\" NUXT_OPENROUTER_API_KEY=\"${NUXT_OPENROUTER_API_KEY:-${OPENROUTER_API_KEY:-}}\" NUXT_OPENROUTER_MODEL=\"${NUXT_OPENROUTER_MODEL:-${OPENROUTER_MODEL:-}}\" NUXT_OPENROUTER_API_ENDPOINT=\"${NUXT_OPENROUTER_API_ENDPOINT:-${OPENROUTER_API_ENDPOINT:-}}\" NUXT_DEEPSEEK_API_KEY=\"${NUXT_DEEPSEEK_API_KEY:-${DEEPSEEK_API_KEY:-}}\" NUXT_DEEPSEEK_MODEL=\"${NUXT_DEEPSEEK_MODEL:-${DEEPSEEK_MODEL:-}}\" NUXT_DEEPSEEK_API_ENDPOINT=\"${NUXT_DEEPSEEK_API_ENDPOINT:-${DEEPSEEK_API_ENDPOINT:-}}\" NUXT_REDIS_URL=\"${NUXT_REDIS_URL:-${REDIS_URL:-}}\"; pnpm --filter @dng/worker start & exec node apps/web/.output/server/index.mjs"]