Docker setup for self-hosting mike, with build-arg fix for Next.js env vars

PasqualeMuraca added a complete Docker setup for running mike locally or on a VPS: separate Dockerfiles for the Express backend and Next.js frontend, a compose file with healthchecks and startup ordering, and a follow-up fix for a real Next.js gotcha around `NEXT_PUBLIC_*` build-time vars.

infrastructuresecurity

The backend Dockerfile (Dockerfile.backend) runs a two-stage build on node:20-alpine. The builder compiles TypeScript to dist/; the runtime stage does npm ci --omit=dev and runs node dist/index.js under dumb-init for signal handling. Frontend follows the same pattern, copying .next/, public/, and next.config.ts from the builder.

In docker-compose.yml, the frontend service uses depends_on: condition: service_healthy - it won't start until the backend passes its /health check. Both services get 20s start periods. The .env.docker.example lists every secret the platform needs: Supabase service-role key, R2 credentials, LLM provider keys (Gemini, Anthropic, OpenAI), Resend, DOWNLOAD_SIGNING_SECRET, and USER_API_KEYS_ENCRYPTION_SECRET.

A follow-up commit (70aaf0c2) fixed a build-time problem. Next.js inlines NEXT_PUBLIC_* vars during npm run build, so they have to be present at image build time. The fix adds ARG NEXT_PUBLIC_SUPABASE_URL and ARG NEXT_PUBLIC_SUPABASE_PUBLISHABLE_DEFAULT_KEY to the builder stage in Dockerfile.frontend, exports them as ENV, and passes them from compose via build.args with placeholder fallbacks. Without this, a freshly built image would have empty Supabase URLs in the client bundle.

There's no database service in compose - Supabase is external. No nginx or TLS either, so this is a building block for self-hosting rather than a turnkey production setup.

So what Worth a look if you want a Docker self-host path for mike. The `NEXT_PUBLIC_*` build-arg pattern is the part most likely to save you time - it's a detail any independent attempt would rediscover the hard way. Check that the env variable names in `.env.docker.example` still match the current upstream before adopting; variable names can drift. Skip if you're deploying to a managed platform like Vercel or Railway.

View this fork on GitHub →

Spotted something wrong? Or know the PR text has fresher detail than the writeup above?

Commits in this thread

7 commits from PasqualeMuraca/mike, oldest first. Source extracted verbatim from the harvested git log.

SHA Subject Author Date
a1b3ea05 Add Docker configuration for backend (Express) Pasquale Muraca 2026-05-13 ↗ GitHub
1d7e7196 Add Docker configuration for frontend (Next.js) Pasquale Muraca 2026-05-13 ↗ GitHub
4865d746 Add docker-compose orchestration for Mike platform Pasquale Muraca 2026-05-13 ↗ GitHub
30a17154 Add Docker environment variables template Pasquale Muraca 2026-05-13 ↗ GitHub
5aa62ed6 Add Docker ignore patterns Pasquale Muraca 2026-05-13 ↗ GitHub
f3e2a1ed Add Docker deployment guide Pasquale Muraca 2026-05-13 ↗ GitHub
70aaf0c2 Pass Supabase env vars to frontend Docker build Pasquale Muraca 2026-05-13 ↗ GitHub
commit body
Add NEXT_PUBLIC_SUPABASE_URL and NEXT_PUBLIC_SUPABASE_PUBLISHABLE_DEFAULT_KEY as build-time ARGs in Dockerfile.frontend and export them as ENV so the frontend build can embed Supabase configuration. Update docker-compose.yml to forward those build args with fallback defaults. Also remove the explicit compose version declaration at the top of docker-compose.yml.

Capture this thread into my fork

Download a single Markdown prompt that tells Claude how to port every commit above into your working tree — adapting paths and structure to match your repo. Run it via claude -p < capture-thread-427.md from inside the repo you want the changes in.

⬇ Download capture-thread-427.md