Run the Docker stack on built output instead of dev servers

compose.yaml overrode the image's own entrypoint to start "tsx watch" and
"next dev" with CHOKIDAR_USEPOLLING and WATCHPACK_POLLING enabled. The
built dist/ and .next from the image were never used, and the polling
watchers kept both containers busy around the clock whether or not
anyone used the application.

Split the two use cases: compose.yaml now runs node dist/server/index.js
and next start with no source mounts and no watchers, and the watching
setup moves to compose.dev.yaml.

next build writes the rewrite destinations from next.config.mjs into
.next/routes-manifest.json, so next start cannot pick up API_INTERNAL_URL
at runtime the way next dev does. Pass it as a build argument.

Give the web container its own probe. The healthcheck hit "/", which
redirects to "/projects", and fetch follows redirects, so every probe
rendered two pages every five seconds. "/health" could not serve as the
probe because next.config.mjs rewrites it to the API. Add "/web-health"
as a route handler, exclude it from the proxy matcher, and relax both
intervals to 30s.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
Julian Appel 2026-08-23 18:25:06 +02:00
parent c6cdfc42d5
commit 906aa751c7
8 changed files with 184 additions and 54 deletions

View file

@ -2,19 +2,27 @@
## Aktueller Status
Es gibt derzeit kein unterstütztes Produktionsdeployment.
Es gibt zwei Compose-Stacks.
`compose.yaml` ist ausschließlich für lokale Entwicklung vorgesehen. Es startet
`tsx watch` und `next dev`, bindet Quellcode vom Host ein und enthält weder TLS,
Authentifizierung, Reverse Proxy, Prozesshärtung noch ein zentral betriebenes
Datenbanksystem. Der Stack darf deshalb nicht als produktionsreif bezeichnet oder
öffentlich erreichbar gemacht werden.
`compose.yaml` startet den gebauten Stand: `node dist/server/index.js` und
`next start`, ohne Quellcode-Mounts und ohne Datei-Watcher. Das ist der Stack
für einen Server.
## Entwicklungs-Topologie
`compose.dev.yaml` startet `tsx watch` und `next dev` und bindet Quellcode vom
Host ein. Die Watcher laufen im Polling-Modus, weil Bind-Mounts unter Windows
und macOS keine inotify-Events durchreichen; das kostet dauerhaft CPU, auch
ohne Benutzeraktivität. Dieser Stack gehört deshalb nur auf einen
Entwicklungsrechner.
Beides enthält weder TLS, Authentifizierung, Reverse Proxy, Prozesshärtung noch
ein zentral betriebenes Datenbanksystem. Der Stack darf deshalb nicht öffentlich
erreichbar gemacht werden.
## Topologie
| Komponente | Port | Healthcheck | Persistenz |
| --- | ---: | --- | --- |
| Next.js Web | 3001 | `GET /` | keine |
| Next.js Web | 3001 | `GET /web-health` | keine |
| Express API | 3000 | `GET /health` | `./data:/app/data` |
| SQLite | Datei | Integritäts-/FK-Prüfung via Backup und Skript | `data/leistungsbilanz.db` |
@ -24,15 +32,22 @@ Verwendete Umgebungsvariablen:
- `API_INTERNAL_URL` internes API-Ziel des Next.js-Rewrites, im Compose-Netz
`http://api:3000`
- `NEXT_TELEMETRY_DISABLED=1`
- `CHOKIDAR_USEPOLLING=true` und `WATCHPACK_POLLING=true` für lokale
Dateibeobachtung in Docker
- `CHOKIDAR_USEPOLLING=true` und `WATCHPACK_POLLING=true` nur in
`compose.dev.yaml`, für Dateibeobachtung über Bind-Mounts hinweg
- `LOG_LEVEL` steuert für beide Dienste die Ausgabestufe des strukturierten
JSON-Loggers (`error`, `warn`, `info`, `verbose`, `debug`), Standard `info`.
Setzbar über eine `.env`-Datei neben `compose.yaml` oder
`LOG_LEVEL=verbose docker compose up`.
Beim API-Start laufen zuerst `npm run db:migrate` und
`npm run db:verify:circuit-schema`.
Beim API-Start laufen zuerst die Migrationen und die Schemaprüfung
(`scripts/run-migrations.js` und `scripts/db-verify-circuit-schema.js`, im
Entwicklungsstack über `npm run db:migrate` und
`npm run db:verify:circuit-schema`).
`API_INTERNAL_URL` wirkt für `next start` zur **Build-Zeit**: `next build`
schreibt die Rewrite-Ziele aus `next.config.mjs` fest in
`.next/routes-manifest.json`. `compose.yaml` reicht den Wert deshalb als
Build-Argument an das Image durch, nicht nur als Laufzeit-Variable.
## Logging