KitchenOwl mit Docker Compose selbst hosten: Einkaufsliste, Rezepte, Haushaltsbuch
Diese Anleitung zeigt, wie Sie KitchenOwl 0.7.10 mit Docker Compose betreiben, den Administrator per Weboberfläche oder API anlegen, Haushalt, Einkaufsliste und Ausgaben erfassen, das Datenverzeichnis sichern, nach einem Totalverlust nachweislich wiederherstellen und von 0.7.9 auf 0.7.10 aktualisieren.
Mit KI erstellt – redaktionelle Prüfung ausstehend
WerbelinksMit * markierte Links sind Werbelinks: Bei einem Kauf erhalten wir eine Provision, der Preis bleibt gleich. Als Amazon-Partner verdiene ich an qualifizierten Verkäufen. Mehr dazu

Wer kauft heute ein, was fehlt im Kühlschrank, und wer hat den letzten Wocheneinkauf bezahlt? KitchenOwl beantwortet diese Fragen in einer gemeinsamen Oberfläche für Haushalte, WGs oder kleine Teams: Einkaufslisten mit Live-Abgleich, eine Rezeptsammlung mit Essensplaner und ein Haushaltsbuch, das Ausgaben zwischen Mitgliedern verrechnet. Das Projekt steht unter AGPL-3.0, bringt eine Web-App sowie Apps für Android und iOS mit und lässt sich als einzelner Container betreiben. Diese Anleitung zeigt die Installation mit Docker Compose, die Ersteinrichtung per Weboberfläche oder API, ein Backup mit echtem Restore nach Totalverlust und das Update von Version 0.7.9 auf 0.7.10. Alle Ausgaben und Fehlermeldungen stammen aus einem eigenen Testlauf.
Voraussetzungen
KitchenOwl besteht aus einem Python-Backend (Flask mit uWSGI) und einer Flutter-Web-App, die im All-in-one-Image zusammen ausgeliefert werden. Im Test belegte der Container im Leerlauf rund 410 MB RAM, das Image ist etwa 1,3 GB groß. Für einen Haushalt oder ein kleines Team genügen:
- Linux-Server, VM oder NAS mit Docker, 1 bis 2 CPU-Kerne und 1 GB freier Arbeitsspeicher, x86_64 oder ARM64
- 3 GB freier Speicher für Image, Datenbank und hochgeladene Bilder
- Docker Engine mit Compose-Plugin v2
- eine eigene Subdomain mit Reverse Proxy und TLS, wenn Apps von unterwegs zugreifen sollen
- ein Speicherziel außerhalb des Servers für die Backups
| Eckdaten | Wert |
|---|---|
| Image | tombursch/kitchenowl:v0.7.10 (aktuelles Release, identisch mit latest) |
| Port | 8080/TCP im Container (Web-App und API) |
| Volume | /data mit database.db und dem Ordner upload |
| Pflicht-Env | JWT_SECRET_KEY |
| Wichtige optionale Env | FRONT_URL, OPEN_REGISTRATION, DISABLE_ONBOARDING, DB_DRIVER, SMTP_*, OIDC_* |
| Lizenz | AGPL-3.0 |
Schritt 1: Projektordner, .env und compose.yaml anlegen
Die Vorlage folgt der Datei docker-compose-single.yml aus dem offiziellen Repository. Drei Änderungen machen sie betriebstauglich: ein festes Versions-Tag statt latest, das Geheimnis in einer separaten .env statt im Klartext in der Compose-Datei und Port 8080 statt 80, damit ein Reverse Proxy davor Platz hat. Die Docker-Hub-Tags tragen das v im Namen; 0.7.10 ohne v gibt es nicht.
mkdir -p /opt/kitchenowl && cd /opt/kitchenowl
cat > .env <<EOF
KITCHENOWL_TAG=v0.7.10
JWT_SECRET_KEY=$(openssl rand -hex 32)
FRONT_URL=https://kitchenowl.example.de
EOF
chmod 600 .env
services:
kitchenowl:
image: tombursch/kitchenowl:${KITCHENOWL_TAG}
restart: unless-stopped
ports:
- "8080:8080"
environment:
- JWT_SECRET_KEY=${JWT_SECRET_KEY}
- FRONT_URL=${FRONT_URL}
volumes:
- kitchenowl_data:/data
volumes:
kitchenowl_data:
FRONT_URL setzt laut Doku den CORS-Header für genau diese Adresse und muss inklusive Schema exakt der späteren URL entsprechen. Wer Frontend und Backend getrennt betreiben oder PostgreSQL statt SQLite nutzen möchte, findet dafür eigene Compose-Dateien im Repository; für einen Haushalt reicht SQLite.
Verifizieren: docker compose config zeigt das aufgelöste Image tombursch/kitchenowl:v0.7.10 und einen 64 Zeichen langen Wert für JWT_SECRET_KEY.
Schritt 2: Container starten
docker compose up -d
docker compose ps
curl -s http://localhost:8080/api/health/8M4F88S8ooi4sMbLBfkkV7ctWwgibW6V
Beim Start führt das Entrypoint-Skript zuerst flask db upgrade aus und importiert danach die Standardartikel, erst dann startet uWSGI. Im Test antwortete die API nach 15 bis 25 Sekunden. Der Pfad mit der langen Zeichenkette ist kein Geheimnis, sondern der fest im Code hinterlegte Health-Endpunkt, den auch der eingebaute Docker-Healthcheck abfragt. Im Log erscheint die Warnung you are running uWSGI as root: Das Image läuft als root, ein Grund mehr, den Port nicht direkt ins Internet zu stellen.
Verifizieren: Der Health-Aufruf liefert {"min_frontend_version":71,"msg":"OK","oidc_provider":[],"version":124}, docker compose ps zeigt nach etwa einer Minute (healthy).
Schritt 3: Ersteinrichtung und Administrator anlegen
Solange kein Benutzer existiert, zeigt die Web-App unter http://SERVER:8080 den Onboarding-Dialog. Der erste angelegte Benutzer erhält Administratorrechte. Dasselbe geht per API, was sich für automatisierte Installationen anbietet:
curl -s http://localhost:8080/api/onboarding
# {"onboarding":true}
curl -s -X POST http://localhost:8080/api/onboarding \
-H 'Content-Type: application/json' \
-d '{"name":"Admin","username":"admin","password":"IHR-PASSWORT","device":"curl"}'
# {"access_token":"...","refresh_token":"..."}
Ein zweiter Onboarding-Aufruf wird danach mit HTTP 403 abgewiesen. Eine offene Selbstregistrierung gibt es standardmäßig nicht: POST /api/auth/signup antwortete im Test mit 404, solange OPEN_REGISTRATION nicht gesetzt ist. Weitere Benutzer legt der Administrator in den Einstellungen an. Wer die Instanz erst nach dem Start konfigurieren will, ohne dass jemand im Netz zuvorkommt, setzt DISABLE_ONBOARDING=true und legt den ersten Benutzer dann manuell an.
Verifizieren: GET /api/onboarding liefert jetzt {"onboarding":false}, eine Anmeldung mit falschem Passwort an POST /api/auth ergibt HTTP 401, ein Aufruf ohne Token {"msg":"Missing Authorization Header"} mit 401.
Schritt 4: Haushalt, Einkaufsliste, Rezept und Ausgabe anlegen
Alle Inhalte hängen an einem Haushalt. Legen Sie ihn in der Web-App über das Plus-Symbol an oder per API mit dem Access-Token aus Schritt 3. Mit "language":"de" importiert KitchenOwl deutsche Standardartikel samt Symbolen und Kategorien.
TOKEN=... # access_token aus Schritt 3
API=http://localhost:8080/api
curl -s -X POST $API/household -H "Authorization: Bearer $TOKEN" \
-H 'Content-Type: application/json' \
-d '{"name":"Musterhaushalt","language":"de","planner_feature":true,"expenses_feature":true}'
# {"id":1,"name":"Musterhaushalt","language":"de",...}
curl -s -X POST $API/household/1/shoppinglist -H "Authorization: Bearer $TOKEN" \
-H 'Content-Type: application/json' -d '{"name":"Wocheneinkauf"}'
curl -s -X POST $API/shoppinglist/2/add-item-by-name -H "Authorization: Bearer $TOKEN" \
-H 'Content-Type: application/json' -d '{"name":"Milch","description":"2 Stück"}'
Jeder neue Haushalt bekommt automatisch eine Liste Default, die eigene Liste erhält deshalb die ID 2. Artikel, die zum Standardkatalog passen, erscheinen mit Symbol; frei benannte Einträge wie ein Testmarker bleiben ohne Symbol. Rezepte legen Sie über POST /api/household/1/recipe an, Ausgaben über POST /api/household/1/expense mit paid_by und paid_for.


Verifizieren: GET /api/shoppinglist/2/items listet die angelegten Artikel und die Web-App zeigt sie unter Einkaufsliste.
Schritt 5: Haushaltsbuch nutzen
Unter Bilanz erfasst jedes Mitglied Ausgaben mit Betrag, Zahler und Begünstigten. KitchenOwl berechnet daraus Salden zwischen den Mitgliedern und eine Monatsübersicht nach Kategorien. Für WGs und Paare ersetzt das die separate Abrechnungs-App, für Firmen ist es eher ein einfaches Kassenbuch für die Teeküche als eine Buchhaltung.

Verifizieren: Die Bilanz zeigt die Ausgabe in der Monatsübersicht, GET /api/household/1/expense liefert sie als JSON.
Schritt 6: Backup anlegen
Alle Daten liegen im Volume unter /data: die SQLite-Datenbank database.db und hochgeladene Bilder in upload. Im laufenden Betrieb nutzt SQLite den WAL-Modus, im Test lagen neben der Datenbank die Dateien database.db-wal und database.db-shm. Ein Kopieren im laufenden Betrieb kann deshalb einen inkonsistenten Stand sichern. Stoppen Sie den Container kurz und archivieren Sie das Volume mit einem Hilfscontainer:
cd /opt/kitchenowl && mkdir -p backup
docker compose stop kitchenowl
docker run --rm -v kitchenowl_kitchenowl_data:/data:ro -v "$PWD/backup":/backup \
alpine tar czf /backup/kitchenowl-data-$(date +%F).tar.gz -C /data .
docker compose start kitchenowl
cp .env backup/
Der Volume-Name setzt sich aus Projektordner und Volume zusammen, prüfen Sie ihn mit docker volume ls. Sichern Sie die .env mit: Ohne den alten JWT_SECRET_KEY funktionieren die Daten zwar weiter, alle angemeldeten Geräte müssen sich aber neu anmelden.
Verifizieren: tar tzvf backup/kitchenowl-data-*.tar.gz zeigt ./database.db und ./upload/; im Test war das Archiv mit Haushalt, Liste, Rezept und Ausgabe rund 28 KB groß.
Schritt 7: Restore nach Totalverlust
Im Test wurde der Ernstfall vollständig durchgespielt: Stack samt Volume mit docker compose down -v gelöscht, neu gestartet und anschließend zurückgesichert. Direkt nach dem Löschen meldete die leere Instanz {"onboarding":true}, der alte Login ergab 401. Das zeigt auch ein Risiko: Eine leere, frisch gestartete Instanz nimmt den ersten Onboarding-Aufruf von jedem an, der sie erreicht. Spielen Sie den Restore deshalb ein, bevor Sie den Port wieder freigeben.
docker compose stop kitchenowl
docker run --rm -v kitchenowl_kitchenowl_data:/data -v "$PWD/backup":/backup alpine \
sh -c 'rm -rf /data/* && tar xzf /backup/kitchenowl-data-DATUM.tar.gz -C /data'
docker compose start kitchenowl
Nach dem Restore lieferte /api/onboarding wieder false, die Anmeldung funktionierte mit dem alten Passwort, und die Einkaufsliste enthielt alle vier Artikel inklusive Marker. Selbst ein vor dem Totalverlust ausgestelltes Token wurde wieder akzeptiert, weil die Token-Datensätze in der Datenbank liegen und der Schlüssel aus der .env unverändert blieb.
Verifizieren: GET /api/onboarding ergibt {"onboarding":false}, GET /api/household listet den Haushalt, die Liste zeigt den vor dem Backup angelegten Marker.
Schritt 8: Update auf eine neue Version
Die Doku empfiehlt docker compose pull und docker compose up -d. Mit festem Tag ändern Sie vorher die Version in der .env. Im Test lief die Instanz zunächst mit 0.7.9 und wurde mit allen Daten auf 0.7.10 gehoben:
cp -a backup backup-vor-update # vorher Backup aus Schritt 6 anlegen
sed -i 's/^KITCHENOWL_TAG=.*/KITCHENOWL_TAG=v0.7.10/' .env
docker compose pull
docker compose up -d
docker compose logs kitchenowl | grep alembic
Das Log zeigte Running upgrade bd383e73ef4d -> 0b10d67750be, der Health-Endpunkt meldete statt Backend-Version 123 nun 124. Liste und Marker blieben erhalten, das bestehende Token blieb gültig. Die Migration lässt sich nicht zurückdrehen, ein Downgrade geht nur über das Backup von vorher. Lesen Sie vor größeren Sprüngen die Seite zur Migration in der Doku.
Verifizieren: docker compose ps zeigt tombursch/kitchenowl:v0.7.10, der Health-Endpunkt "version":124.
Troubleshooting
Die folgenden Fehlerbilder wurden im Test gezielt ausgelöst:
| Symptom | Ursache | Lösung |
|---|---|---|
Anmeldung und jeder API-Aufruf liefern Something went wrong mit HTTP 500, im Log RuntimeError: JWT_SECRET_KEY or flask SECRET_KEY must be set when using symmetric algorithm "HS256" | JWT_SECRET_KEY ist gesetzt, aber leer, etwa durch eine fehlende Zeile in der .env | Wert in der .env eintragen, docker compose up -d |
| Login funktioniert, obwohl kein Secret konfiguriert ist | Variable fehlt ganz, das Backend nutzt den Standardwert super-secret aus dem Quellcode | Eigenes Secret setzen; mit dem öffentlich bekannten Wert lassen sich Tokens fälschen |
Alle Geräte abgemeldet, API meldet {"msg":"Signature verification failed"} mit HTTP 422 | JWT_SECRET_KEY wurde geändert, etwa nach einem Restore ohne alte .env | Alten Schlüssel zurückspielen; im Test war das Token danach wieder gültig (200) |
Bind for 0.0.0.0:8080 failed: port is already allocated | Port ist von einem anderen Dienst belegt | Anderen Host-Port wählen, danach docker compose up -d --force-recreate |
docker.io/tombursch/kitchenowl:0.7.10: not found | Tag ohne v | v0.7.10 verwenden |
| Container läuft, Port antwortet in den ersten Sekunden nicht | Migration und Import der Standardartikel laufen vor dem Webserver | 20 bis 30 Sekunden warten, Log beobachten |
Hinter einem Reverse Proxy muss dieser WebSockets durchreichen, sonst werden laut Doku manche API-Aufrufe spürbar langsamer. Die Web-App speichert ihr Login-Token über die Bibliothek flutter_secure_storage, deren Web-Variante laut Quellcode nur in sicheren Kontexten arbeitet, also unter HTTPS oder localhost. Betreiben Sie KitchenOwl deshalb hinter einem Proxy mit TLS und HSTS, wie es auch die offizielle Doku empfiehlt.
Häufige Fragen
Brauche ich PostgreSQL?
Für einen Haushalt oder ein kleines Team nicht. SQLite ist der Standard und hält alles in einer Datei, was Backups einfach macht. PostgreSQL wird über DB_DRIVER=postgresql und die DB_*-Variablen eingebunden, ein Beispiel liegt als docker-compose-postgres.yml im Repository.
Funktionieren die Smartphone-Apps mit dem eigenen Server?
Ja, in den Apps lässt sich die Adresse des eigenen Servers eintragen. Tragen Sie dort die HTTPS-Adresse Ihres Reverse Proxys ein.
Kann ich Rezepte von Webseiten importieren?
Ja, KitchenOwl liest Rezepte über eine URL ein. Optional lässt sich laut Doku ein Sprachmodell über LLM_MODEL und passende API-Schlüssel für die Zutatenerkennung anbinden. Dann gehen Rezepttexte an den gewählten Anbieter.
Wie melde ich Benutzer zentral an?
KitchenOwl unterstützt OpenID Connect über die OIDC_*-Variablen mit einem eigenen Identity Provider. Mit DISABLE_USERNAME_PASSWORD_LOGIN=true ist danach nur noch die Anmeldung über OIDC möglich.
Was ist mit dem Image kitchenowl-backend?
Das ist die getrennte Variante mit separatem Frontend-Container kitchenowl-web. Sie ist sinnvoll, wenn Sie das Backend skalieren wollen. Für den normalen Betrieb reicht das All-in-one-Image aus dieser Anleitung.
Fazit
KitchenOwl ist schnell eingerichtet und deckt Einkaufsliste, Rezepte, Essensplanung und Ausgaben in einer App ab, ohne dass Daten bei einem Cloud-Dienst liegen. Die wichtigste Stellschraube ist der JWT_SECRET_KEY: Ohne ihn läuft die Instanz mit einem öffentlich bekannten Schlüssel, ein leerer Wert legt die Anmeldung lahm. Das Backup ist dank SQLite ein einziges Archiv, das im Test einen kompletten Datenverlust samt Testmarker zurückgebracht hat. Wer einen festen Versions-Tag pflegt und vor jedem Update sichert, hat mit KitchenOwl wenig Wartungsaufwand.
Weiterführende Anleitungen und Quellen
- Tandoor Recipes mit Docker Compose selbst hosten
- Mealie mit Docker Compose installieren und sichern
- Grocy: Vorratsverwaltung mit Docker Compose
- Traefik als Reverse Proxy mit HTTPS einrichten
- Backups mit autorestic und restic
- KitchenOwl auf GitHub
- Offizielle Doku: Installation
- Offizielle Doku: Umgebungsvariablen
- Release v0.7.10
- Image-Tags auf Docker Hub


