Docker
Levantá el stack completo (Postgres, Redis, server, client) con un solo comando para desarrollo local o builds production-like.
El repo trae dos archivos Compose. Elegí el que coincida con lo que necesitás:
| Archivo | Propósito | Qué incluye |
|---|---|---|
docker-compose.dev.yml | Desarrollo local. Hot reload, source mounted, secrets efímeros. | Postgres 16 · Redis 7 · server (Bun + --hot) · client (next dev) |
docker-compose.yml | Build production-like. Imágenes standalone, sin source mount, sin DB. | imagen del server · imagen del client (vos traés tu Postgres + Redis) |
Si querés probar el boilerplate end-to-end sin provisionar nada, usá
docker-compose.dev.yml— levanta el stack completo.
Quick start (desarrollo)
Desde la raíz del repo:
docker compose -f docker-compose.dev.yml upEse único comando no buildea nada — pulla imágenes públicas y montea tu working tree. Esperá ~30 segundos para que bun install termine adentro de ambos containers.
Obtenés:
| Servicio | URL / Port | Notas |
|---|---|---|
| Client | http://localhost:3004 | Next.js con hot reload |
| Server | http://localhost:3005 | Express en Bun, hot reload vía bun --hot |
| Postgres | postgresql://postgres:postgres@localhost:5432/boilerplate | Usuario/pass/db son seedeados por el archivo compose |
| Redis | redis://localhost:6379 | Usado por BullMQ + store de rate-limit |
El server lee su config desde variables de entorno horneadas en el archivo compose — no se necesita .env para arrancar.
[!NOTE] El container del client necesita dos URLs de la API en compose:
NEXT_PUBLIC_API_URL=http://localhost:3005para el browser, yINTERNAL_API_URL=http://server:3005para los fetches del middleware / RSC de Next.js que corren dentro del container. Setups single-host (Vercel, bare-metal) solo necesitanNEXT_PUBLIC_API_URL— el middleware cae a esa cuandoINTERNAL_API_URLno está seteada.
Primer run — aplicar migrations
El archivo compose arranca el server pero no corre las migrations de Prisma automáticamente (para que puedas elegir la estrategia correcta para tu branch). En el primer boot, en una segunda terminal:
docker compose -f docker-compose.dev.yml exec server \
bunx --cwd apps/server prisma migrate deployPara un schema fresco durante desarrollo podés usar migrate dev en su lugar — eso crea una migration nueva desde tu schema.prisma actual:
docker compose -f docker-compose.dev.yml exec server \
bunx --cwd apps/server prisma migrate devPrimer run — seed de roles + permissions
El boilerplate trae un script de seed que crea los roles default owner / admin / member para el módulo IAM. Corrélo una vez:
docker compose -f docker-compose.dev.yml exec server \
bun apps/server/prisma/seed.tsListo. Registrate en http://localhost:3004/register y vas a aterrizar en el dashboard.
Comandos comunes
# Tail de logs del server (el más útil)
docker compose -f docker-compose.dev.yml logs -f server
# Abrir un shell dentro del container del server (debug Prisma, correr scripts, etc.)
docker compose -f docker-compose.dev.yml exec server sh
# Conectarse a Postgres con psql
docker compose -f docker-compose.dev.yml exec postgres \
psql -U postgres -d boilerplate
# Parar todo (los containers quedan — `up` la próxima vez es más rápido)
docker compose -f docker-compose.dev.yml stop
# Bajar todo INCLUYENDO el volume de Postgres (destruye data)
docker compose -f docker-compose.dev.yml down -vServicios opcionales
El compose de dev mantiene la surface chica intencionalmente. Si necesitás los adapters opcionales que vienen con el boilerplate, agregalos vos mismo:
- BullMQ Bull Board — arranca dentro del container del server automáticamente cuando
REDIS_URLestá seteado; visitá http://localhost:3005/admin/queues. - Mailpit / MailHog para capturar emails de dev — agregá un service block, después seteá
EMAIL_FROM=dev@localy (si usás Resend) dejáRESEND_API_KEYsin definir para que elLogEmailProvidercaiga. - MongoDB — el archivo compose tiene un block
mongodbcomentado. Descomentalo, seguí ADR-002 para cambiar el provider de Prisma, y actualizáDATABASE_URL.
Build production-like
El otro archivo compose (docker-compose.yml) buildea imágenes inmutables de server y client — útil para verificar que tu código pasa el mismo build pipeline que CI usa, o para pushear a un registry.
# Provisioná tu propio Postgres + Redis primero, después exportá sus URLs:
export DATABASE_URL=postgresql://...
export REDIS_URL=redis://...
export BETTER_AUTH_SECRET=$(openssl rand -hex 32)
docker compose up --buildEsto no incluye una database — el compose de prod asume que estás corriendo contra Postgres managed (Supabase, Neon, RDS, etc.) y Redis managed (Upstash, ElastiCache, etc.). Ver la guía de Supabase para un setup de un solo vendor.
La imagen del server corre prisma migrate deploy en el boot, así que las migrations se aplican automáticamente contra el DATABASE_URL configurado.
Troubleshooting
port is already allocated — algo en tu host está usando 3004, 3005, 5432, o 6379. O paralo o remapeá el port host-side en el archivo compose ("3104:3004").
El server arranca pero cada request da 500 con "Cannot find module '@prisma/client'" — el cliente de Prisma no se generó. El container de dev corre bun install en el boot que dispara el postinstall hook; si compitió con la primera request, reiniciá el server: docker compose -f docker-compose.dev.yml restart server.
El hot reload no levanta cambios en macOS — el filesystem polling de Docker Desktop es lento. El archivo compose montea todo el repo con volumes: - .:/app; si ves lag, cambiá Docker Desktop a VirtioFS (Settings → General → Virtual Machine Manager).
Las migrations fallan con "database does not exist" — el healthcheck de Postgres reporta ready antes de que la database boilerplate se cree en el primer boot. Esperá 5 segundos y reintentá el comando migrate.
bun install corre cada vez y es lento — eso es esperado: el compose montea un volume para node_modules para que persista entre restarts, pero el primer install paga el costo completo de red. Boots subsiguientes reusan el volume.
Un container desapareció a mitad de corrida y volvió solo — server, worker y client llevan restart: unless-stopped en el compose de dev. Es intencional: suites E2E pesadas pueden OOM-killear el proceso de Bun bajo flujos de auth concurrentes + encolados de BullMQ, y el auto-restart te ahorra correr docker start a mano. Si un container reinicia repetidamente, mirá los logs (logs -f <service>) — tenés un crash real, no un one-off.