SSD Nodes Learn 🎉 VPS ab $5.50/Monat
Anleitungen Matt ConnorVon Matt Connor · Aktualisiert 2026-08-13

Mealie auf einem VPS selbst hosten mit Docker Compose

Installieren Sie Mealie auf Ihrem VPS mit Docker Compose: Rezepte per URL importieren, Wochenplan und Einkaufsliste nutzen sowie nginx, TLS und Backups einrichten.

Was ein selbst gehosteter Rezeptmanager leistet

Ein selbst gehosteter Rezeptmanager speichert Ihre Rezepte in einer Datenbank auf einem Server, den Sie selbst verwalten. Mealie ist die Lösung, für die sich die meisten Haushalte entscheiden. Sie fügen die Adresse einer Rezeptseite ein. Mealie liest daraus die Zutaten, die Zubereitungsschritte, die Anzahl der Portionen und die Kochzeit und lässt den Beschreibungstext sowie die Werbung weg. In Ihrer Sammlung landet nur das Rezept.

Der restliche Funktionsumfang der Anwendung ist überschaubar. Es gibt einen Wochenplan, in den Sie Rezepte per Drag-and-drop einfügen. Außerdem gibt es eine Einkaufsliste, die aus diesem Plan erstellt wird. Jede Person, die kocht, erhält einen eigenen Login. Die gesamte Anwendung läuft in einem Container. Zwischen Anfragen bleibt sie untätig. Deshalb reicht ein kleiner VPS aus, ohne dass die Anwendung die Ressourcen spürbar belastet.

Diese Anleitung verwendet Docker Compose. Wenn Ihnen die Begriffe services: und volumes: noch nicht bekannt sind, lesen Sie zuerst wie Docker-Compose-Dateien aufgebaut sind. Alles Folgende besteht aus einer Compose-Datei und vier Befehlen.

Mealie mit Docker Compose installieren

Mealie veröffentlicht seine Images in der GitHub Container Registry. Im Juli 2026 lautet der aktuelle stabile Tag v3.22.0. Fixieren Sie eine Version, statt latest zu verwenden: Mit latest kann ein docker compose pull an einem beliebigen Tag Sie auf eine Datenbankmigration bringen, auf die Sie nicht vorbereitet waren.

sudo mkdir -p /srv/mealie
cd /srv/mealie
sudo nano docker-compose.yml
services:
  mealie:
    image: ghcr.io/mealie-recipes/mealie:v3.22.0
    container_name: mealie
    restart: always
    ports:
      - "127.0.0.1:9925:9000"
    deploy:
      resources:
        limits:
          memory: 1000M
    volumes:
      - mealie-data:/app/data/
    environment:
      ALLOW_SIGNUP: "false"
      PUID: 1000
      PGID: 1000
      TZ: Europe/Amsterdam
      BASE_URL: https://recipes.example.com

volumes:
  mealie-data:

Vor dem Start sollten Sie zwei Zeilen prüfen.

Der Port ist als 127.0.0.1:9925:9000 und nicht als 9925:9000 eingetragen. Der Container lauscht intern auf Port 9000, und der Host leitet Port 9925 dorthin weiter. Wenn Sie diese Weiterleitung an die Loopback-Adresse binden, kann nginx Mealie erreichen, das Internet jedoch nicht. Docker schreibt eigene Regeln in den Paketfilter. Daher ist ein einfacher 9925:9000 von außen erreichbar, selbst wenn Ihre Firewall den Port als geschlossen ausweist. Dieses Verhalten sollten Sie einmal verstehen: warum veröffentlichte Docker-Ports ufw umgehen.

BASE_URL muss exakt die öffentliche Adresse sein, die Sie verwenden werden, einschließlich Schema und ohne abschließenden Schrägstrich. Mealie erstellt daraus Links zum Zurücksetzen von Passwörtern und Einladungslinks. Wenn Sie http://localhost:9925 eintragen, enthält die Einladung an Ihren Partner einen Link, der nur auf dem Server selbst funktioniert.

Starten Sie den Dienst und überwachen Sie den ersten Start.

sudo docker compose up -d
sudo docker compose logs -f mealie

Beim ersten Start erstellt Mealie die SQLite-Datenbank und führt die Migrationen aus. Das dauert einige Sekunden. Wenn sich die Logausgabe stabilisiert und keine Migrationszeilen mehr ausgegeben werden, prüfen Sie die Anwendung lokal.

curl -I http://127.0.0.1:9925

Ein 200 OK bedeutet, dass die Anwendung läuft. Connection refused bedeutet, dass der Container nicht läuft: Führen Sie sudo docker compose ps aus und prüfen Sie den Exit-Code. Ein Container, der mit Code 137 beendet wurde, wurde wegen einer Überschreitung des Speicherlimits von 1000M beendet. Das kommt bei den kleinsten Tarifen vor.

Erste Anmeldung und Deaktivieren der offenen Registrierung

Das Standardkonto ist changeme@example.com mit dem Passwort MyPassword. Melden Sie sich damit an und ändern Sie anschließend beide Werte sofort, weil diese Kombination in der Dokumentation steht und daher in jedem Scanner enthalten ist.

ALLOW_SIGNUP: "false" in der Compose-Datei ist beabsichtigt. Bei aktivierter Registrierung kann jeder, der die Adresse findet, ein Konto in Ihrer Rezeptsammlung erstellen. Bei deaktivierter Registrierung fügen Sie Personen über den Administrationsbereich hinzu. Dabei wird ein Einladungslink erzeugt, den Sie selbst versenden. Dieser Link wird aus BASE_URL erstellt. Deshalb ist dieser Wert wichtig. Wenn Sie mehrere Anwendungen auf demselben Server betreiben und für alle dasselbe Passwort verwenden möchten, kann Mealie die Anmeldung an einen externen Identity Provider wie eine selbst gehostete Authentik-Instanz delegieren.

Mealie fasst Benutzer in einem Haushalt zusammen. Alle Personen in einem Haushalt teilen sich die Rezeptsammlung, den Speiseplan und die Einkaufsliste. Das entspricht den Anforderungen einer Familie. Getrennte Haushalte auf demselben Server behalten getrennte Sammlungen. Das ist für eine Wohngemeinschaft sinnvoll, wenn niemand einer Meinung über Sardellen ist.

Der Importer ist der Grund für diesen Ablauf

Öffnen Sie die Rezeptsammlung, wählen Sie das Erstellen eines Rezepts aus einer URL und fügen Sie einen Link ein. Mealie ruft die Seite ab und sucht nach strukturierten Rezeptdaten, also nach dem maschinenlesbaren Block, den die meisten Rezeptseiten für Suchmaschinen einbetten. Wenn dieser Block vorhanden ist, wird das Rezept sauber und sofort importiert.

Sie können auch ein Bild oder eingefügten Klartext importieren. Damit lässt sich beispielsweise ein Foto einer Kochbuchseite verarbeiten. Diese Varianten laufen über einen langsameren Pfad und sollten anschließend geprüft werden, da ein handgeschriebener Bruch leicht falsch erkannt wird.

Massenimporte starten Sie auf demselben Bildschirm. Fügen Sie eine Liste von Adressen ein, jeweils eine pro Zeile. Mealie arbeitet sie anschließend im Hintergrund ab. Eine Sammlung mit zweihundert Lesezeichen lässt sich so in einem Durchgang übertragen.

Mahlzeitenpläne und Einkaufsliste

Der Mahlzeitenplaner ist ein Kalender. Ziehen Sie ein Rezept auf einen Tag, um es einzuplanen. Die Einkaufsliste übernimmt anschließend die Zutaten der geplanten Rezepte in eine gemeinsame Liste und fasst doppelte Einträge zusammen. Wenn zwei Rezepte Zwiebeln benötigen, wird daher eine Zeile statt zwei angezeigt.

Die Liste ist auf Ihrem Smartphone im Geschäft als aktuelle Seite verfügbar. Da sie auf Ihrem eigenen Server gespeichert ist, sehen alle Personen im Haushalt gleichzeitig dieselbe Liste. Wenn eine Person die Milch abhakt, wird sie auch auf dem Bildschirm der anderen Person entfernt.

nginx und TLS vorschalten

Mealie verwendet unverschlüsseltes HTTP und bietet keine eigene Zertifikatsverwaltung. Beenden Sie TLS in dem vorgeschalteten nginx. Verweisen Sie zuerst mit einem DNS-A-Record auf Ihren Server, da der Zertifikatsschritt diesen Namen überprüft.

sudo apt update && sudo apt install -y nginx
sudo nano /etc/nginx/sites-available/mealie
server {
    listen 80;
    server_name recipes.example.com;

    client_max_body_size 64M;

    location / {
        proxy_pass http://127.0.0.1:9925;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;
    }
}
sudo ln -s /etc/nginx/sites-available/mealie /etc/nginx/sites-enabled/
sudo nginx -t && sudo systemctl reload nginx

Die Ausgabe von nginx -t, syntax is ok und test is successful ist die entscheidende Prüfung. Führen Sie den Reload erst aus, wenn diese Prüfung erfolgreich ist. Andernfalls bleibt bei einer fehlerhaften Konfiguration die alte Konfiguration aktiv, und der Fehler wird bis zum nächsten Neustart nicht sichtbar.

client_max_body_size 64M ist erforderlich, weil der nginx-Standardwert 1 MB beträgt. Beim Hochladen eines Rezeptfotos oder beim Wiederherstellen eines Backups über den Browser wird ein größerer Request-Body übertragen. Ohne diese Zeile erhalten Sie von nginx einen 413 Request Entity Too Large, nicht von Mealie. Daher wird überhaupt nichts im Anwendungslog protokolliert.

Stellen Sie anschließend das Zertifikat aus. Dieser Schritt und der Timer für die Verlängerung werden unter ein Let's-Encrypt-Zertifikat für nginx mit certbot ausstellen beschrieben.

sudo apt install -y certbot python3-certbot-nginx
sudo certbot --nginx -d recipes.example.com

Certbot schreibt den Server-Block so um, dass er auf 443 lauscht, und fügt eine Weiterleitung von Port 80 hinzu. Rufen Sie die Website über https:// auf und prüfen Sie, ob der Browser das Zertifikat akzeptiert. Wenn Mealie geladen wird, seine eigenen Links Sie jedoch zu http:// weiterleiten, steht in BASE_URL weiterhin http. Korrigieren Sie den Wert und führen Sie anschließend sudo docker compose up -d aus, um den Container mit dem neuen Wert neu zu erstellen.

Mealie kann nicht unter einem Unterpfad wie example.com/recipes bereitgestellt werden, weil das Frontend nicht aus einem Unterpfad heraus bereitgestellt werden kann. Verwenden Sie eine Subdomain.

Backups und was eine Wiederherstellung tatsächlich bewirkt

Alles, was Mealie verwaltet, liegt in /app/data/ innerhalb des Containers. Dabei handelt es sich um das mealie-data-Volume. Wenn Sie dieses Volume kopieren, kopieren Sie Rezepte, Bilder und Datenbank gemeinsam.

sudo docker volume ls
sudo docker compose stop mealie
sudo docker run --rm -v mealie_mealie-data:/data -v "$PWD":/backup \
  alpine tar czf /backup/mealie-data.tgz -C /data .
sudo docker compose start mealie

Der Volume-Name enthält den Projektnamen als Präfix. Der Projektname entspricht dem Verzeichnis, in dem die Compose-Datei liegt. Unter /srv/mealie lautet der Volume-Name mealie_mealie-data. Deshalb verwenden Sie im ersten Befehl docker volume ls: Verwenden Sie den ausgegebenen Namen und nicht den Namen aus dieser Anleitung. Der Container muss vorher gestoppt werden. SQLite befindet sich häufig mitten in einem Schreibvorgang. Eine laufende Kopie kann daher möglicherweise nicht lesbar wiederhergestellt werden.

Mealie verfügt außerdem über eine eigene Backup-Seite im Administrationsbereich. Dort wird ein portables Archiv erstellt, das die Datenbank als JSON und Ihre Bilder enthält. Verwenden Sie diese Funktion, wenn Sie den Dienst auf einen anderen Server verschieben. Das Archiv bleibt auch bei einem Versionswechsel nutzbar, während eine Kopie der Rohdateien möglicherweise nicht mehr funktioniert. Die Wiederherstellung ist absichtlich destruktiv: Die aktuelle Datenbank wird vor dem Laden des Archivs gelöscht. Dieser Vorgang kann nicht rückgängig gemacht werden. Nach Abschluss werden Sie abgemeldet.

Keine der beiden Kopien ist ein Backup, solange sie auf demselben Server liegt. Übertragen Sie das Archiv regelmäßig an einen anderen Speicherort. Dafür sind verschlüsselte Off-Server-Backups mit restic vorgesehen.

Mealie aktualisieren

cd /srv/mealie
sudo nano docker-compose.yml
sudo docker compose pull
sudo docker compose up -d
sudo docker compose logs -f mealie

Erhöhen Sie die festgelegte Version in der Datei. Rufen Sie anschließend das Image ab und erstellen Sie den Container neu. Migrationen werden beim ersten Start des neuen Images ausgeführt. Erstellen Sie vor einem Wechsel auf eine neue Hauptversion eine Kopie des Volumes. Eine Migration, die zur Hälfte fehlschlägt, kann eine Datenbank hinterlassen, die das vorherige Image nicht mehr öffnen kann. Lesen Sie die Release Notes für alle Versionen zwischen Ihrer aktuellen und der neuen Version.

Wenn der Import fehlschlägt

Einige Websites veröffentlichen überhaupt keine strukturierten Rezeptdaten. Mealie importiert dann einen Titel mit einer leeren Zutatenliste. Das lässt sich nicht durch eine Konfiguration ändern. Fügen Sie den Rezepttext stattdessen manuell ein.

Andere Fehler werden durch einen Bot-Schutz vor der Rezept-Website verursacht. Dieser liefert Mealie eine Challenge-Seite anstelle des Rezepts. Mealie gibt sich bereits als Browser aus und wechselt den User-Agent, um dies zu reduzieren. Wenn eine Website weiterhin blockiert, bestehen laut Dokumentation zwei Möglichkeiten: Leiten Sie den Scraper über einen Proxy mit besserer IP-Reputation, oder betreiben Sie eine FlareSolverr-Instanz, die die Challenge in einem echten Browser löst. Beide Optionen sind optional. Beide werden über Umgebungsvariablen im Container festgelegt.

Ein Import, der fehlschlägt, weil Ihr Server die Website überhaupt nicht erreichen kann, ist ein anderes Problem. Testen Sie die Verbindung direkt auf dem Server mit curl -I https://the-site.example/recipe und lesen Sie die Statuszeile, bevor Sie den Scraper verantwortlich machen.

Wo es hineinpasst

Mealie ist eine gute erste selbst gehostete Anwendung für einen Haushalt, weil die Menschen, mit denen Sie zusammenleben, sie nutzen werden, ohne dass Sie sie darum bitten müssen. Die Aufgabe ähnelt dem Betrieb Ihrer eigenen Fotobibliothek mit Immich, ist jedoch deutlich ressourcenschonender. Außerdem gehört Mealie zur größeren Liste der Dienste, deren Self-Hosting sich in diesem Jahr lohnt. Ein kleiner Server kann beides aufnehmen. Immich ist nicht der einzige Kandidat für diese zweite Aufgabe. Wenn Sie noch entscheiden, unterscheiden sich die Speicheranforderungen und Backup-Befehle von PhotoPrism und Immich ausreichend, um sie zu lesen, bevor Sie den restlichen Speicherplatz anderweitig vergeben.

FAQ

Warum schlägt der Import einer Rezept-URL fehl?

Dafür gibt es zwei häufige Ursachen. Entweder veröffentlicht die Seite keine strukturierten Rezeptdaten. Dann findet der Scraper nichts, und Sie erhalten einen Titel ohne Zutaten. Oder eine Bot-Schutzschicht vor der Website liefert anstelle des Rezepts eine Challenge-Seite zurück. Im zweiten Fall kann Mealie auf einen Proxy mit besserem Ruf der IP-Adresse oder auf eine selbst gehostete FlareSolverr-Instanz verwiesen werden, die die Challenge in einem echten Browser löst. Bestätigen Sie mit curl -I, dass Ihr Server die Seite überhaupt erreichen kann, bevor Sie etwas ändern.

Benötige ich PostgreSQL, oder reicht SQLite aus?

Für einen Haushalt reicht SQLite aus. Es ist die Standardeinstellung. Wechseln Sie zu PostgreSQL, wenn das Datenverzeichnis auf netzwerkgebundenem Speicher liegt. SQLite auf einem Netzwerkdateisystem verursacht Fehler wegen gesperrter Datenbanken und kann die Datei beschädigen. Für Wiederherstellungen unter PostgreSQL muss der Datenbankbenutzer ein Superuser sein, da die Wiederherstellung vor dem Laden des Archivs alle Daten löscht.

Kann ich Mealie ohne Domainnamen betreiben?

Ja, in Ihrem eigenen Netzwerk. Setzen Sie BASE_URL auf die Adresse, die Sie tatsächlich eingeben werden, beispielsweise http://192.168.1.20:9925, und verzichten Sie auf nginx. Einladungs- und Links zum Zurücksetzen des Passworts werden aus BASE_URL erstellt. Ein falscher Wert erzeugt daher Links, die niemand sonst öffnen kann. Machen Sie die Anwendung nicht über unverschlüsseltes HTTP im Internet erreichbar, da die Anmeldedaten dann im Klartext übertragen werden.

Wie gebe ich meiner Familie eigene Anmeldungen?

Lassen Sie ALLOW_SIGNUP auf "false" gesetzt und fügen Sie Personen im Administrationsbereich hinzu. Dadurch wird ein Einladungslink erstellt, den Sie ihnen senden. Ordnen Sie alle Personen, die eine Küche gemeinsam nutzen, demselben Haushalt zu. So teilen sie Rezepte, den Speiseplan und die Einkaufsliste. Getrennte Haushalte auf einem Server behalten separate Sammlungen.

Was passiert mit meinen Rezepten, wenn ich Mealie nicht mehr betreibe?

Sie bleiben verfügbar. Die Administrationssicherung schreibt Ihre Daten als JSON. Mealie kann Rezepte außerdem als reine Markdown-Dateien exportieren. Diese bleiben in jedem Texteditor lesbar, ohne dass dafür Software erforderlich ist. Erstellen Sie einen Export, bevor Sie ihn benötigen, und prüfen Sie, dass Sie ihn öffnen können.