Die Adresse zeigte auf /var/run/clamav/clamd.sock in einem geteilten
Volume. Das offizielle clamav-Image legt seinen Unix-Socket aber unter
/tmp im eigenen Container an - nachgemessen, das Verzeichnis im Volume
bleibt leer. Jeder Verbindungsversuch lief damit in ENOENT, und die
Statusseite meldete die Virenpruefung dauerhaft als gestoert.
Folgenreich war das nicht nur fuer die Anzeige: ohne erreichbaren Scanner
bleiben Uploads auf "Pruefung unvollstaendig" liegen und brauchen die
Freigabe von Hand. Die Kette hat also gehalten, aber niemand kam durch.
Dasselbe Image oeffnet ab Werk TCPSocket 3310. Darueber laeuft die
Anbindung jetzt; der Port ist im Stack nicht veroeffentlicht und nur im
internen Netz erreichbar. CLAMAV_ADDRESS nimmt weiterhin einen Pfad an,
falls irgendwo doch ein Unix-Socket erreichbar ist.
Das Socket-Volume faellt weg, es enthielt nie etwas. Der Scanner sieht
ausserdem die Dateiablage nicht mehr: uebertragen wird per INSTREAM durch
die Verbindung, gelesen hat er dort nie etwas.
Geprueft gegen ein laufendes clamd, einschliesslich EICAR. Diese Pruefung
bleibt als abschaltbarer Teil der Suite stehen:
CLAMAV_LIVE=clamav:3310 pnpm --filter @afis/web test pruefkette
Die uebrigen neuen Pruefungen decken das Zerlegen der Adresse ab. Sie
haetten den Fehler nicht gefunden - eine Adresse, die auf nichts zeigt,
ist syntaktisch tadellos. Nur eine echte Verbindung beantwortet das.
Zwei Fehler aus der ersten Inbetriebnahme.
Der Seed wurde ueber einen geratenen Pfad in tsx aufgerufen. Jetzt ueber
node_modules/.bin/tsx - der Eintrag stimmt unabhaengig davon, wie das Paket
seine Dateien anordnet.
Das Auslagerungs-Backup braucht RESTIC_REPOSITORY und RESTIC_PASSWORD und
beendet sich ohne beides sofort wieder, also im Sekundentakt neu. Es steht
jetzt wie der Tunnel in einem Profil und startet nur mit
`docker compose --profile offsite up -d`. Ohne Angabe laufen sieben Dienste.
Auf einer frischen Datenbank lief nur `migrate deploy`. Die Tabellen standen
damit, aber leer: keine Freigabestufen, keine Status-Etiketten, keine Fallarten,
keine Nummernkreise. Die Anwendung startet so zwar, aber es laesst sich keine
Akte anlegen - es gibt keine Vorlage.
Der Seed besteht ausschliesslich aus upserts und darf deshalb bei jedem Start
mitlaufen. Dafuer liegen jetzt tsx, der Prisma-Client und der Quelltext des
db-Pakets mit im Migrator-Verzeichnis des Images, statt beim Start aus dem Netz
geholt zu werden.
Neuere Fassungen von edoburu/pgbouncer lauschen auf 5432 statt auf 6432. Der
Dienst lief damit einwandfrei, aber der Healthcheck fragte 6432 ab, blieb ohne
Antwort und meldete "unhealthy" - woraufhin web und worker gar nicht erst
starteten. DATABASE_URL haette danach dasselbe Problem gehabt.
LISTEN_PORT steht jetzt fest auf 6432. Das haelt ausserdem sichtbar
auseinander, ob eine Verbindung ueber den Pool geht oder mit
DIRECT_DATABASE_URL an ihm vorbei.
Wer die Anwendung auf einem anderen Port erreichen will, musste bisher die
compose.yaml aendern - eine verfolgte Datei. Jeder `git pull` lief danach in
"local changes would be overwritten", und die Aenderung ging beim Verwerfen
verloren.
Jetzt steht der Host-Port als WEB_PORT in der .env, die ohnehin nicht verfolgt
wird. Ohne Angabe bleibt es bei 3000.
Der Tunnel war fest im Stack und damit Voraussetzung, obwohl er nur eine von
mehreren Moeglichkeiten ist, die Anwendung von aussen erreichbar zu machen. Wer
einen eigenen Reverse Proxy betreibt, brauchte ihn nie - und wollte womoeglich
gerade nicht von einem fremden Dienst abhaengen.
Er steht jetzt in einem Profil und startet nur mit
`docker compose --profile cloudflare up -d`. Ohne Angabe bleibt er aus, und der
Stack laeuft mit acht Diensten.
`docker compose up` fragt zuerst in einer Registry nach, auch wenn das Image
lokal gebaut wird, und warnt dann "pull access denied". Mit `pull_policy: never`
nimmt Compose gleich das lokale Image.
Betrifft vor allem den Start-Knopf in Dockge, der `up -d` ohne vorheriges
`build` ausfuehrt.
Die drei eigenen Images standen als ghcr.io-Adressen mit einem Platzhalter im
Namen. Gebaut haetten sie nur GitHub Actions, und das Repository liegt nicht
auf GitHub - die Images gab es also nirgends, und wer den Platzhalter uebersah,
bekam "repository name must be lowercase".
Jetzt stehen die Bau-Anweisungen in der compose.yaml selbst. Das ist auch fuer
Dockge das richtige: dessen Editor zeigt nur diese eine Datei, eine
compose.override.yml waere unsichtbar gewesen und haette spaeter niemand mehr
erklaeren koennen.
Dazu ein Hinweis im Kopf der Datei, dass PORT und Healthchecks den Port *im*
Container meinen. Wer die Anwendung auf dem Host anders erreichen will, aendert
nur die linke Zahl bei ports.
compose.yaml aus docs/11-betrieb.md, ergänzt um Healthchecks für db-backup und
offsite-backup. Das Dokument verlangt für jeden Dienst einen, die abgedruckte
Fassung hatte für die beiden keinen, und ohne ihn zeigt Dockge nur "läuft",
auch wenn seit Wochen nichts mehr gesichert wird.
Die Migration läuft im Startkommando von web, nicht in einem Init-Container.
Die Prisma-CLI samt Schema-Engine liegt dafür unter /app/migrator im Image:
ein Container, der nur startet, wenn gerade eine Registry erreichbar ist, wäre
auf einem Homeserver die falsche Bauart.
Alle drei Images gebaut und laufen gegen eine echte Postgres-Instanz geprüft:
web wendet Migrationen an und liefert /api/health aus, worker schreibt sein
Lebenszeichen, pdf rendert ein echtes PDF.
Die CI prüft Lint, Typen, Tests und E2E gegen den Standalone-Server, also so,
wie der Container startet, und hält das Bündel-Budget aus
docs/09-performance-und-ux.md ein.
Phase 0 aus TASKS.md ist damit abgehakt.