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.
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.
Spotted something wrong? Or know the PR text has fresher detail than the writeup above?