Docker
Build the Node standalone image from root and separate public build args from secrets.
Use Docker when you operate a server or container host. Build the Node app (apps/web), connect its services, and expose it through your HTTPS reverse proxy.
Before You Start
Have Docker, the full repository source, a separate production Postgres database, real mail, and credentials for enabled storage/payments. Prepare a domain and HTTPS proxy. The development Postgres setup is not a production operations configuration. Run build commands from the repository root.
Build
docker build -f apps/web/Dockerfile -t musemvp:latest --build-arg NEXT_PUBLIC_SITE_URL=https://app.example.com --build-arg NEXT_PUBLIC_DEFAULT_LOCALE=en .Add build args for enabled NEXT_PUBLIC_GOOGLE_ONE_TAP_CLIENT_ID, NEXT_PUBLIC_TURNSTILE_SITE_KEY, and NEXT_PUBLIC_EMAIL_SUPPORT. Next inlines public values; runtime updates do not replace rebuilding.
The Dockerfile uses Node 22, pnpm, and Turbo prune, then runs node apps/web/server.js. The image neither runs production migrations nor starts Postgres automatically.
Database and Runtime
Prepare a dedicated production environment file, e.g. apps/web/.env.production, following Environment Variables. Set real auth/mail/billing/storage credentials; production rejects MAIL_PROVIDER=console. Do not include development seed credentials or pass secrets as build args.
Database commands default to apps/web/.env.local. Before migration, verify that file/injected connection actually targets the intended database:
pnpm db:status
pnpm db:migrate
docker run -d --name musemvp-app --restart unless-stopped --env-file apps/web/.env.production -p 127.0.0.1:3000:3000 musemvp:latest
docker logs musemvp-appInside a container, DATABASE_URL localhost means that container, not the host or another Postgres container. Use reachable hosts/Compose service names and configure volumes, TLS, backups, and rollback.
Verify HTTPS, OAuth callbacks, cookies, actual mail, sandbox payments, and storage before launch. See Release Checklist.
Check Your Result
The loopback mapping above suits an HTTPS proxy on the same host. Configure your proxy to forward to that port; use a private network instead if the proxy is containerized. Open your HTTPS domain and confirm startup logs, login, real mail, saved data, and enabled uploads/payments. Database data should remain independent of replacing the app container.
Use a separate release checkout for production migrations: root scripts load .env.local, not the runtime .env.production file. Set up database backups, persistent database volumes where applicable, monitoring, and image rollback. Do not seed development accounts in production.
Common Problems
| Problem | Fix |
|---|---|
| Missing workspace files | Build from root with -f apps/web/Dockerfile |
| Database unreachable | Check container networking and database host, not container localhost |
| Public URL stays wrong | Rebuild with the correct public build args |
| Proxy returns 502 | Inspect container logs, port binding, and proxy network |