# MediaHub: Container stürzt beim Start ab (Wettlauf der gunicorn-Worker um db.json.tmp)

**URL:** <https://forum.espuino.de/t/mediahub-container-stuerzt-beim-start-ab-wettlauf-der-gunicorn-worker-um-db-json-tmp/4813>\
**Category:** Software\
**Created:** [4. Oktober 2026 um 18:06 UTC](https://forum.espuino.de/t/mediahub-container-stuerzt-beim-start-ab-wettlauf-der-gunicorn-worker-um-db-json-tmp/4813 "2026-10-04T18:06:38Z")\
**Posts on this page:** 2\
**Page:** 1

<div class="post-metadata">

**Author:** ![Jonas1](https://forum.espuino.de/letter_avatar_proxy/v4/letter/j/eb9ed0/32.png) [@Jonas1](https://forum.espuino.de/u/Jonas1)\
**Post date:** [4. Oktober 2026 um 18:06 UTC](https://forum.espuino.de/t/mediahub-container-stuerzt-beim-start-ab-wettlauf-der-gunicorn-worker-um-db-json-tmp/4813/1 "2026-10-04T18:06:38Z")

</div>

Hallo zusammen,

erst mal vielen Dank für den MediaHub! Da ich 3 Espuinos mit identischen Medien haben, habe ich schon lange auf sowas gehofft. Da ich etwas ähnliches noch nie gemacht habe, habe ich mich von Claude an der Hand nehmen lassen, was erstaunlich gut funktioniert hat.

Dabei bin ich auf das Problem gestoßen, dass der Docker immer wieder unerwartet beendet wurde und neu gestartet ist.

Mit Claude konnte ich einen Workaround entwickeln. Aber ich wollte den Fehler zumindest melden, damit jemand mit mehr Ahnung vom Thema vielleicht mal sehen kann ob da was dran ist.

Ich betreibe den Docker auf einem Synology NAS (Container Manager, docker compose, Dockerfile unverändert, 2 gunicorn-Worker).

Problem  
Der Container bricht beim Start ab und startet dann neu:

File „/app/app.py“, line 44, in  
store = store\_lib.Store(DATA\_DIR)  
File „/app/store.py“, line 69, in \_write  
os.replace(tmp\_path, self.db\_path)  
FileNotFoundError: [Errno 2] No such file or directory: ‚/data/db.json.tmp‘ → ‚/data/db.json‘  
[ERROR] Reason: Worker failed to boot.

Wann tritt es auf?

- Beim allerersten Start (noch keine db.json im data-Ordner).
- Später wieder, nachdem ich rund 190 Karten für 3 Geräte angelegt hatte (db.json ca. 2 MB). Das Schreiben dauert dann länger, der Fehler tritt häufiger auf.

Ursache (Vermutung aus dem Code)  
Store() wird beim Import in jedem Worker ausgeführt. \_migrate\_legacy\_cards() ruft \_mutate() auf, und das schreibt immer, auch wenn nichts zu migrieren ist. Beide Worker schreiben in dieselbe Zwischendatei db.json.tmp: Worker A benennt sie per os.replace() um, Worker B findet sie dann nicht mehr. Das \_lock schützt nur innerhalb eines Prozesses, nicht zwischen den Workern. Dasselbe kann theoretisch auch im laufenden Betrieb passieren, wenn zwei Anfragen gleichzeitig bei verschiedenen Workern landen.

Nachgestellt (von Claude)  
In einem Test mit sechs parallel schreibenden Prozessen (je 300 Schreibvorgänge) trat der Fehler mit dem Originalcode in jedem Prozess mehrfach auf, einmal auch mit exakt dem Absturz oben. Mit einem eindeutigen Namen der Zwischendatei gab es keinen Fehler mehr.

Workaround  
Bei mir läuft es stabil, wenn gunicorn mit --preload gestartet wird (docker-compose.override.yml mit command: gunicorn … --workers 2 --preload …). Dann läuft die Store-Initialisierung nur einmal im Hauptprozess.

Vorschlag für einen Fix

1. In store.py, \_write(): einen eindeutigen Namen für die Zwischendatei verwenden, z. B.  
tmp\_path = f"{self.db\_path}.{os.getpid()}.tmp"
2. \_migrate\_legacy\_cards()/\_mutate() nur schreiben lassen, wenn sich tatsächlich etwas geändert hat.
3. Langfristig eine Dateisperre (z. B. fcntl.flock) statt des reinen Prozess-Locks. Bei zwei Workern kann sonst bei gleichzeitigem Lesen-Ändern-Schreiben eine Änderung verloren gehen.

Nochmal vielen Dank für das super Feature!

Viele Grüße

Jonas

---

<div class="post-metadata">

**Author:** ![biologist](https://forum.espuino.de/user_avatar/forum.espuino.de/biologist/32/66_2.png) [@biologist](https://forum.espuino.de/u/biologist)\
**Post date:** [4. Oktober 2026 um 18:57 UTC](https://forum.espuino.de/t/mediahub-container-stuerzt-beim-start-ab-wettlauf-der-gunicorn-worker-um-db-json-tmp/4813/2 "2026-10-04T18:57:13Z")

</div>

Danke — deine Analyse stimmt in allen drei Punkten. Beide Fixes sind eingecheckt.

Ein Detail macht es noch blöder als die Fehlermeldung vermuten lässt: Nach dem `os.replace()` von Worker A verschwindet nur der _Name_ `db.json.tmp`, nicht die Inode. Der offene Deskriptor von Worker B zeigt danach direkt auf `db.json` — B schreibt seinen Rest also in die gerade veröffentlichte Datenbank hinein, und erst danach scheitert sein eigenes `os.replace()`. Der Absturz ist das Symptom, nicht der Schaden. Nachgestellt mit zwei `fork()`-Kindern: Bei gleich langen Datensätzen ist die Änderung des einen Workers still verloren, die Datei bleibt gültiges JSON. Bei unterschiedlich langen bleibt der Schwanz des längeren stehen und man bekommt `JSONDecodeError: Extra data`. Je größer die Datei, desto größer das Zeitfenster — deshalb fällt es dir erst bei 2 MB auf.

Es betraf übrigens nicht nur den ersten Start: `_migrate_legacy_cards()` lief in jedem Worker bei jedem Start und schrieb die ganze Datei neu, auch ohne etwas zu migrieren. (Punkt 2).

Umgesetzt ist jetzt alles drei — prozess-eindeutiger Temp-Name und `fcntl.flock` um das Read-Modify-Write (von @albert0815, der den Fix schon fertig hatte, danke dafür), dazu das Schreiben nur noch bei echter Änderung.

```bash
git pull
docker compose up -d --build

```

Das `--build` ist nötig, sonst läuft das alte Image weiter. `--preload` kannst du danach wieder rausnehmen — es hat den Startfehler verhindert, gegen den Wettlauf im Betrieb (dein zweiter Fall mit den 190 Karten) aber nicht geholfen.

Eine Bitte noch: Schau einmal über deine Zuweisungen. Ein stiller Lost Update hinterlässt gültiges JSON, der Datei sieht man also nichts an — auffallen würde nur eine Karte oder ein Gerät, das fehlt. Mit einem Backup des `./data`-Volumes von vorher geht der Vergleich schneller.
