- Created 01-01-SUMMARY.md with execution results - Updated STATE.md with plan completion and metrics - Updated ROADMAP.md marking plan 01-01 complete - Updated REQUIREMENTS.md marking INFRA-01, INFRA-02, INFRA-03 complete
9.9 KiB
phase: 01-foundation-portal-shell plan: 01 subsystem: infra tags: [docker, docker-compose, traefik, nestjs, nextjs, prisma, postgresql, pnpm, turborepo, biome, typescript, tailwindcss, monorepo]
Dependency graph
requires: [] provides:
- Docker Compose stack with 4 services (traefik, web, api, db)
- pnpm monorepo with Turborepo build orchestration
- NestJS API with /health endpoint
- Next.js frontend with standalone Docker output
- PostgreSQL database on internal network
- Three-network segmentation (frontend-net, backend-net, data-net)
- Prisma schema with Tenant model foundation
- @tessera/shared package with APP_NAME and HealthResponse type
- TypeScript base config with strict mode
- Biome linter/formatter config affects: [01-02-portal-shell, 01-03-portal-shell, phase-2-auth, phase-3-modules]
Tech tracking
tech-stack: added: [pnpm@9.15.0, turbo@2.9.18, biome@2.5.0, typescript@5.9.3, nestjs@11, nextjs@15.5.19, prisma@6, postgresql@16, traefik@2.11, tailwindcss@4, react@19] patterns: [monorepo-workspace, multi-stage-docker-build, network-segmentation, health-check-endpoint]
key-files: created: - package.json - pnpm-workspace.yaml - turbo.json - tsconfig.base.json - biome.json - docker-compose.yml - docker-compose.dev.yml - apps/api/src/main.ts - apps/api/src/health/health.controller.ts - apps/api/prisma/schema.prisma - apps/api/Dockerfile - apps/web/src/app/layout.tsx - apps/web/src/app/page.tsx - apps/web/Dockerfile - packages/shared/src/index.ts modified: - .gitignore - .dockerignore
key-decisions:
- "Used Traefik v2.11 instead of v3.4 due to Docker API version incompatibility on host"
- "API Dockerfile copies full monorepo node_modules structure to resolve pnpm workspace symlinks"
- "Next.js standalone output in monorepo runs from apps/web/server.js (not root server.js)"
- "Traefik placed on both frontend-net and backend-net for routing to both web and api services"
patterns-established:
- "Monorepo structure: apps/* for deployables, packages/* for shared code"
- "Multi-stage Docker builds with monorepo root as build context"
- "Network segmentation: frontend-net (public), backend-net (internal routing), data-net (internal: true for DB)"
- "Health check pattern: /health endpoint for container orchestration"
- "Workspace dependencies via workspace:* protocol"
requirements-completed: [INFRA-01, INFRA-02, INFRA-03]
Metrics
duration: 16min completed: 2026-06-18
Phase 1 Plan 01: Walking Skeleton Summary
Docker Compose stack with pnpm monorepo, NestJS API health endpoint, Next.js frontend, PostgreSQL on internal network, Traefik reverse proxy with three-network segmentation
Performance
- Duration: 16 min
- Started: 2026-06-18T08:01:17Z
- Completed: 2026-06-18T08:17:51Z
- Tasks: 3
- Files modified: 25
Accomplishments
- Full Docker Compose stack starts with
docker compose upserving web at localhost:80 and API at localhost:80/api/health - pnpm monorepo with Turborepo build orchestration, all packages type-check clean
- Three-network Docker segmentation: PostgreSQL isolated on internal data-net, web cannot reach DB directly
- Multi-stage Docker builds for both NestJS and Next.js with non-root users
Task Commits
Each task was committed atomically:
- Task 1: Monorepo scaffold with root configs and shared package -
b75d7c3(feat) - Task 2: NestJS API with health endpoint and Prisma schema -
04c78b4(feat) - Task 3: Next.js app + Docker Compose stack with network segmentation -
2c12878(feat) - Chore: Add tsbuildinfo to gitignore -
dbe2d50(chore)
Files Created/Modified
package.json- Root monorepo config with pnpm workspace and Turborepo scriptspnpm-workspace.yaml- Workspace definition for apps/* and packages/*turbo.json- Build orchestration with task dependency graphtsconfig.base.json- Shared TypeScript config with strict modebiome.json- Linter and formatter configuration.gitignore- Ignores node_modules, .next, dist, .turbo, .env, tsbuildinfo.env.example- Template for environment variables.dockerignore- Excludes build artifacts from Docker contextpackages/shared/src/index.ts- APP_NAME constant and HealthResponse interfaceapps/api/src/main.ts- NestJS bootstrap listening on port 3001apps/api/src/app.module.ts- Root module with ConfigModule and HealthModuleapps/api/src/health/health.controller.ts- GET /health returning {status, timestamp}apps/api/src/health/health.module.ts- Health feature moduleapps/api/prisma/schema.prisma- PostgreSQL datasource with Tenant modelapps/api/Dockerfile- Multi-stage build, non-root nestjs userapps/web/src/app/layout.tsx- Root layout with de localeapps/web/src/app/page.tsx- Placeholder page with "Tessera" headingapps/web/src/app/globals.css- Tailwind CSS v4 importapps/web/next.config.ts- Standalone output modeapps/web/postcss.config.mjs- @tailwindcss/postcss pluginapps/web/Dockerfile- Multi-stage build, non-root nextjs userdocker-compose.yml- 4 services, 3 networks, Traefik routingdocker-compose.dev.yml- Volume mounts for hot reload
Decisions Made
- Traefik v2.11 instead of v3.4: Traefik v3.3+ ships with Docker API client v1.24 which is below the minimum v1.40 required by Docker Engine 29.x on this host. Downgraded to v2.11 which works correctly. Future upgrade possible when Traefik v3 fixes API negotiation.
- Traefik on backend-net: Added Traefik to backend-net (in addition to frontend-net) so it can route /api/* requests to the API container. This is necessary since Traefik discovers and routes to containers only on shared networks.
- API Dockerfile with full node_modules: pnpm monorepo uses symlinks in per-package node_modules pointing to root. The runner stage copies both root and per-package node_modules to preserve the link structure.
- Next.js standalone monorepo path: In a monorepo, Next.js standalone output places server.js at apps/web/server.js (not root). CMD adjusted accordingly.
Deviations from Plan
Auto-fixed Issues
1. [Rule 1 - Bug] Fixed TypeScript moduleResolution conflict in API tsconfig
- Found during: Task 2 (NestJS API)
- Issue: Base tsconfig has moduleResolution "bundler" which is incompatible with module "commonjs" in API tsconfig
- Fix: Added moduleResolution "node" override in apps/api/tsconfig.json
- Files modified: apps/api/tsconfig.json
- Verification: pnpm turbo type-check --filter=@tessera/api passes
2. [Rule 3 - Blocking] Added @tessera/shared workspace dependency to API
- Found during: Task 2 (NestJS API)
- Issue: Health controller imports HealthResponse from @tessera/shared but no workspace dependency declared
- Fix: Added "@tessera/shared": "workspace:*" to apps/api/package.json dependencies
- Files modified: apps/api/package.json
- Verification: Type-check passes, import resolves correctly
3. [Rule 1 - Bug] Fixed API Dockerfile for pnpm monorepo node_modules
- Found during: Task 3 (Docker Compose)
- Issue: API container crashed with "Cannot find module @nestjs/core" - runner stage copied empty per-package node_modules
- Fix: Restructured Dockerfile to copy root node_modules and preserve pnpm workspace symlink structure
- Files modified: apps/api/Dockerfile
- Verification: Container starts and /health endpoint responds
4. [Rule 1 - Bug] Fixed Next.js standalone path for monorepo
- Found during: Task 3 (Docker Compose)
- Issue: Web container crashed with "Cannot find module /app/server.js" - standalone output in monorepo places server at apps/web/server.js
- Fix: Updated Dockerfile COPY paths and CMD to use apps/web/ prefix
- Files modified: apps/web/Dockerfile
- Verification: Container starts and serves pages
5. [Rule 1 - Bug] Fixed Next.js listening on container hostname instead of 0.0.0.0
- Found during: Task 3 (Docker Compose)
- Issue: Next.js standalone server bound to container hostname, not accessible from other containers
- Fix: Added HOSTNAME: "0.0.0.0" environment variable in docker-compose.yml web service
- Files modified: docker-compose.yml
- Verification: curl http://localhost:80 returns Tessera page via Traefik
6. [Rule 1 - Bug] Fixed Traefik Docker API version incompatibility
- Found during: Task 3 (Docker Compose)
- Issue: Traefik v3.4 and v3.3 use Docker API v1.24 which Docker Engine 29.x rejects (minimum 1.40)
- Fix: Downgraded to Traefik v2.11 which negotiates API version correctly
- Files modified: docker-compose.yml
- Verification: Traefik starts, discovers services, routes requests correctly
Total deviations: 6 auto-fixed (4 bugs, 1 blocking, 1 blocking) Impact on plan: All fixes necessary for the stack to function. No scope creep. The Docker-related issues are common monorepo + standalone pitfalls documented in the research as Pitfall 4.
Issues Encountered
- Traefik v3.x Docker provider API version negotiation is broken with Docker Engine 29.x -- this is a known upstream issue. Resolved by using Traefik v2.11 which correctly handles version negotiation.
User Setup Required
None - no external service configuration required. The stack runs with default development credentials.
Known Stubs
apps/web/src/app/page.tsx- Placeholder page with "Tessera" heading and "Portal wird geladen..." text. Intentional skeleton -- Plan 01-02 replaces with full portal shell.
Next Phase Readiness
- Docker Compose stack is fully operational for Plan 01-02 (Portal Shell with i18n, theming, layout)
- All build tooling (Turborepo, Biome, TypeScript) is configured and working
- Prisma schema ready for migrations when database is needed
- Both apps containerized with multi-stage builds
Self-Check: PASSED
All 15 key files verified present. All 4 commit hashes verified in git log.
Phase: 01-foundation-portal-shell Completed: 2026-06-18