feat(01-01): Next.js app + Docker Compose stack with network segmentation

- Next.js 15 app with Tailwind CSS v4, standalone output for Docker
- Root layout with de locale, page with Tessera placeholder
- Multi-stage Dockerfile for web with monorepo root context
- Docker Compose with 4 services: traefik, web, api, db
- Three segregated networks: frontend-net, backend-net, data-net (internal)
- Traefik v2.11 reverse proxy routing / to web, /api to api
- API strip prefix middleware for clean routing
- PostgreSQL 16-alpine with health checks and named volume
- Fixed API Dockerfile for pnpm monorepo node_modules structure
- Fixed Next.js standalone path for monorepo (apps/web/server.js)
- Fixed Traefik Docker API version compatibility
- Network segmentation: web NOT on data-net, api NOT on frontend-net
This commit is contained in:
2026-06-18 10:17:21 +02:00
parent 04c78b4bdc
commit 2c12878cb1
12 changed files with 1079 additions and 6 deletions
+90
View File
@@ -0,0 +1,90 @@
services:
traefik:
image: traefik:v2.11
ports:
- "80:80"
command:
- "--api.insecure=true"
- "--providers.docker=true"
- "--providers.docker.exposedbydefault=false"
environment:
DOCKER_API_VERSION: "1.40"
networks:
- frontend-net
- backend-net
volumes:
- /var/run/docker.sock:/var/run/docker.sock:ro
web:
build:
context: .
dockerfile: apps/web/Dockerfile
environment:
HOSTNAME: "0.0.0.0"
networks:
- frontend-net
- backend-net
depends_on:
api:
condition: service_healthy
labels:
- "traefik.enable=true"
- "traefik.http.routers.web.rule=PathPrefix(`/`)"
- "traefik.http.services.web.loadbalancer.server.port=3000"
- "traefik.http.routers.web.priority=1"
- "traefik.docker.network=tessera-ctl_frontend-net"
api:
build:
context: .
dockerfile: apps/api/Dockerfile
networks:
- backend-net
- data-net
depends_on:
db:
condition: service_healthy
environment:
DATABASE_URL: ${DATABASE_URL:-postgresql://tessera:tessera_dev@db:5432/tessera}
healthcheck:
test: ["CMD", "wget", "--no-verbose", "--tries=1", "--spider", "http://localhost:3001/health"]
interval: 10s
timeout: 5s
retries: 3
start_period: 10s
labels:
- "traefik.enable=true"
- "traefik.http.routers.api.rule=PathPrefix(`/api`)"
- "traefik.http.services.api.loadbalancer.server.port=3001"
- "traefik.http.routers.api.priority=2"
- "traefik.http.middlewares.api-strip.stripprefix.prefixes=/api"
- "traefik.http.routers.api.middlewares=api-strip"
- "traefik.docker.network=tessera-ctl_backend-net"
db:
image: postgres:16-alpine
networks:
- data-net
volumes:
- pgdata:/var/lib/postgresql/data
environment:
POSTGRES_USER: tessera
POSTGRES_PASSWORD: ${DB_PASSWORD:-tessera_dev}
POSTGRES_DB: tessera
healthcheck:
test: ["CMD-SHELL", "pg_isready -U tessera"]
interval: 5s
timeout: 3s
retries: 5
networks:
frontend-net:
driver: bridge
backend-net:
driver: bridge
data-net:
driver: bridge
internal: true
volumes:
pgdata: