Eine Kamera am Vogelhaus, ein Rechner der erkennt wer da war — und eine Sammlung, die sich von allein fuellt. Zwei neuronale Netze: eines findet den Vogel im Bild, eines bestimmt die Art. 148 heimische Arten, auf Deutschland eingeschraenkt und neu normiert — ohne diese Einschraenkung wird aus einer Kohlmeise eine nordamerikanische Schwesterart. Daraus entsteht ein Sammelalbum: nummerierte Seiten, gefundene Arten in Farbe, fehlende als Schatten, dazu Erstfang, Punkte, Stufen und 15 Abzeichen. Laeuft vollstaendig auf eigener Hardware. Kein Cloud-Dienst, kein Konto, keine Telemetrie; die Bilder verlassen das eigene Netz nicht. Zwei getrennt deploybare Teile: die Sammlung als Django-Anwendung, die Erkennung als eigener Dienst. Der Erkennungs-Dienst fragt selbst an und braucht deshalb weder eine erreichbare Adresse noch einen offenen Port — er laeuft hinter einem gewoehnlichen Router. Dabei: Firmware fuer Raspberry Pi und ESP32-S3, Anbindung an Home Assistant ueber MQTT, Oberflaeche auf Deutsch und Englisch, Einstellungen je Kamera aus dem Browser, und ein Weg fuer Fotos von unterwegs. AGPL-3.0.
171 lines
7.0 KiB
Markdown
171 lines
7.0 KiB
Markdown
# Die Web-App auf Coolify stellen
|
|
|
|
Für den Umzug der Sammlung von einem Rechner im Heimnetz auf eine öffentlich
|
|
erreichbare Instanz. Der Detector zieht **nicht** mit um — er braucht
|
|
Sichtverbindung zur Kamera und bleibt zu Hause; er bekommt am Ende nur eine
|
|
neue Adresse.
|
|
|
|
Vorlage ist `docker-compose.yml` im Wurzelverzeichnis, als
|
|
**Docker-Compose-Anwendung aus dem Git-Repository**.
|
|
|
|
## Was dieses Setup von einem Ein-Container-Deploy unterscheidet
|
|
|
|
Zwei Dienste, und der Eingang ist **nicht** die Django-App:
|
|
|
|
```
|
|
Traefik ──▶ medien (nginx, Port 8080) ──┬──▶ /media/… direkt vom Volume
|
|
└──▶ alles andere ▶ web (Gunicorn, 8000)
|
|
```
|
|
|
|
🔑 **Die Domain muss auf `medien` zeigen, nicht auf `web`.** Django liefert
|
|
unter `DJANGO_DEBUG=0` keine Medien aus, und Coolify stellt keinen Webserver
|
|
davor, der das übernehmen könnte — Traefik routet auf einen Container und kennt
|
|
das Volume nicht. Zeigt die Domain auf `web`, läuft die Seite scheinbar
|
|
normal, aber **jedes Vogelfoto ist 404**: die Sammlung ist das Einzige, was
|
|
dieses Projekt ist, und sie wäre leer. Die ausführliche Begründung steht in
|
|
`nginx-media.conf`.
|
|
|
|
## 1. Anwendung anlegen
|
|
|
|
* Typ **Docker Compose**, Quelle das Git-Repository, Branch `main`,
|
|
Compose-Datei `docker-compose.yml`.
|
|
* Ist das Repository privat, muss Coolify über die Gitea-Anbindung
|
|
(GitHub-App-Äquivalent bzw. Deploy-Key) darauf zugreifen können — dasselbe
|
|
Verfahren wie bei den anderen Projekten auf derselben Instanz.
|
|
|
|
## 2. Domain auf den Dienst `medien`
|
|
|
|
In Coolify hat bei einer Compose-Anwendung **jeder Dienst sein eigenes
|
|
Domain-Feld**. Die Domain gehört an `medien` (Port 8080). `web` bekommt
|
|
**keine** Domain — der Dienst wird nur intern angesprochen.
|
|
|
|
Danach prüfen, dass der DNS-Eintrag der Domain auf die Coolify-Maschine zeigt.
|
|
|
|
## 3. Umgebungsvariablen
|
|
|
|
Pflicht:
|
|
|
|
| Variable | Wert |
|
|
|---|---|
|
|
| `DJANGO_ALLOWED_HOSTS` | die Domain, z. B. `vogelalbum.gaertner.onl` |
|
|
| `DJANGO_CSRF_TRUSTED_ORIGINS` | dieselbe Domain **mit** `https://` |
|
|
|
|
Optional, aber meist gewollt:
|
|
|
|
| Variable | Wert |
|
|
|---|---|
|
|
| `DJANGO_SECRET_KEY` | leer lassen ist in Ordnung — der Entrypoint erzeugt einen und legt ihn aufs Volume, er bleibt über Neustarts stabil |
|
|
| `VOGELALBUM_INGEST_TOKEN` | **leer lassen**, wenn jede Kamera im Admin registriert ist und ihren eigenen Token hat. Leer schaltet den Sammel-Token ab, und das ist für eine öffentliche Instanz das Richtige |
|
|
| `VOGELALBUM_LESE_TOKEN` | nur nötig, wenn Home Assistant `/api/zustand/` abfragt. **Leer heißt: der Endpunkt antwortet mit 401** — wer die Einbindung zu Hause schon hat und das hier vergisst, sucht den Fehler im Heimnetz |
|
|
|
|
Nicht anfassen, die stehen fest in `docker-compose.yml`:
|
|
`DJANGO_DEBUG=0`, `VOGELALBUM_LIVE_ERLAUBT=0`, `SQLITE_DIR=/data`,
|
|
`MEDIA_ROOT=/data/media`.
|
|
|
|
⚠️ **Lässt man Coolify ein Geheimnis erzeugen, darf es kein `$` enthalten.**
|
|
Docker Compose liest `$xxx` als Variable und zerstört den Wert dabei still.
|
|
Selbst erzeugen mit `openssl rand -base64 32`.
|
|
|
|
🔒 `VOGELALBUM_LIVE_ERLAUBT=0` ist der Grund, warum auf der öffentlichen Instanz
|
|
kein Livebild einschaltbar ist — auch nicht mit einem Fehlklick im Admin. Ein
|
|
Bild aus dem eigenen Garten soll nicht an einem Häkchen hängen.
|
|
|
|
## 4. Volume
|
|
|
|
Das Compose-File deklariert das benannte Volume `vogelalbum-data` und hängt es in
|
|
beide Dienste (`web` beschreibbar, `medien` nur lesend). Coolify übernimmt das;
|
|
es muss nichts von Hand angelegt werden.
|
|
|
|
⚠️ **Dort liegen Datenbank *und* Fotos.** `SQLITE_DIR` und `MEDIA_ROOT` zeigen
|
|
beide auf `/data`. Fehlt das Volume oder zeigt eine der beiden Variablen
|
|
woandershin, ist die Sammlung nach jedem Deploy weg.
|
|
|
|
## 5. Erster Start
|
|
|
|
Der Entrypoint erledigt `migrate`, `collectstatic`, `seed_dex` und holt
|
|
anschließend die Referenzfotos der Arten von Wikipedia.
|
|
|
|
⏱️ **Der erste Start dauert deshalb mehrere Minuten** — rund 150 Arten, und
|
|
Wikimedia drosselt, weshalb zwischen den Abrufen gewartet wird. Das ist kein
|
|
hängender Deploy. Jeder weitere Start überspringt alle Arten, die schon ein
|
|
Foto haben, und kostet keine einzige Anfrage mehr.
|
|
|
|
Danach steht eine leere Sammlung: Artenkatalog vollständig, keine Sichtungen.
|
|
|
|
## 6. Eine bestehende Sammlung mitnehmen
|
|
|
|
Wer von einer Heiminstanz umzieht, bringt Datenbank und Fotos mit. Beides liegt
|
|
beim Umzugspaket im selben Layout, in dem es auf dem Volume liegen muss:
|
|
|
|
```
|
|
db.sqlite3
|
|
media/sightings/…
|
|
media/crops/…
|
|
media/referenz/…
|
|
```
|
|
|
|
🔑 **Eine 1:1-Kopie, keine Neueinlieferung über die API.** Nur so kommen die
|
|
Kamera-Tokens, die Schwellwerte je Kamera, Handbestimmungen, Abzeichen und
|
|
Erstfang-Vermerke unverändert mit. Die Kameras zu Hause liefern danach mit
|
|
**ihrem bisherigen Token** weiter — es muss nichts neu registriert werden.
|
|
|
|
Auf der Heiminstanz ein konsistentes Paket bauen (`VACUUM INTO` kopiert die
|
|
Datenbank sauber, auch während die App läuft):
|
|
|
|
```sh
|
|
cd /pfad/zum/vogelalbum
|
|
python - <<'PY'
|
|
import sqlite3
|
|
sqlite3.connect('web/db.sqlite3').execute("VACUUM INTO '/tmp/db.sqlite3'")
|
|
PY
|
|
mkdir -p /tmp/umzug/media
|
|
mv /tmp/db.sqlite3 /tmp/umzug/
|
|
cp -r web/media/sightings web/media/crops web/media/referenz /tmp/umzug/media/
|
|
tar czf /tmp/vogelalbum-daten.tgz -C /tmp/umzug db.sqlite3 media
|
|
```
|
|
|
|
Das Paket auf die Coolify-Maschine legen, dann dort:
|
|
|
|
```sh
|
|
# Anwendung anhalten (in Coolify: Stop) — SQLite nicht im Betrieb tauschen.
|
|
VOL=$(docker volume ls -q | grep vogelalbum-data | head -1)
|
|
docker run --rm -v "$VOL":/data -v /root:/rein:ro alpine \
|
|
sh -c 'tar xzf /rein/vogelalbum-daten.tgz -C /data && chown -R 0:0 /data && ls -la /data'
|
|
# Anwendung wieder starten.
|
|
```
|
|
|
|
Der Container läuft als `root`, daher `chown -R 0:0`.
|
|
|
|
## 7. Nach dem Umzug prüfen
|
|
|
|
```sh
|
|
curl -sI https://DEINE-DOMAIN/ | head -1 # 200
|
|
curl -sI https://DEINE-DOMAIN/sichtungen/ | head -1
|
|
```
|
|
|
|
Und im Browser: **eine Sichtungskarte muss ihr Foto zeigen.** Ein 404 auf
|
|
`/media/…` heißt, die Domain hängt am falschen Dienst (Schritt 2).
|
|
|
|
## 8. Die Kameras zu Hause umstellen
|
|
|
|
Jeder Detector und jede weitere Anbindung zeigt noch auf die alte Adresse im
|
|
Heimnetz. Umzustellen ist:
|
|
|
|
* **jeder Detector**: `VOGELALBUM_API` auf `https://DEINE-DOMAIN` (in
|
|
`~/.vogelalbum.env` bzw. `--api`), danach neu starten. Laufen zwei Kameras,
|
|
gibt es zwei Detector-Prozesse — womöglich auf zwei verschiedenen Maschinen.
|
|
* **Home Assistant**, falls eingebunden: neue Adresse und `VOGELALBUM_LESE_TOKEN`.
|
|
|
|
Dass eine Kamera angekommen ist, sieht man im Admin an `letzter_kontakt` —
|
|
der Konfigurationsabruf setzt es, auch ohne einen einzigen Vogel.
|
|
|
|
## 9. Was bewusst nicht mit umzieht
|
|
|
|
* **Der Detector.** Er braucht die Modelle, Rechenzeit und das Kameranetz.
|
|
* **Das Livebild.** `VOGELALBUM_LIVE_ERLAUBT=0`, siehe oben.
|
|
* **Der Störungswächter** (`vogelalbum-stoerungen.service`). 🔑 Er darf nicht auf
|
|
derselben Maschine laufen wie das, was er überwacht, und nicht an derselben
|
|
Stromversorgung: beim Stromausfall am 30.09.2026 war er von 22:31 bis 06:19
|
|
stumm, weil er selbst tot war. Wer das Ausbleiben von Sichtungen bemerken
|
|
soll, muss von außen schauen.
|