SSD Nodes Learn Hosting plans →
Przewodniki Matt ConnorAutor: Matt Connor

Mealie na własnym VPS: instalacja przez Docker Compose

Instrukcja uruchomienia menedżera przepisów Mealie na serwerze VPS przy użyciu Docker Compose. Konfiguracja obejmuje automatyczne pobieranie danych, listy zakupów oraz TLS.

Funkcje własnego menedżera przepisów

Własny menedżer przepisów przechowuje dane w bazie na posiadanym serwerze, a Mealie jest rozwiązaniem najczęściej wybieranym w gospodarstwach domowych. Po wklejeniu adresu strony z przepisem, Mealie odczytuje składniki, instrukcje, liczbę porcji oraz czas przygotowania, pomijając opisy i reklamy. W kolekcji zapisywane są wyłącznie informacje o potrawie.

Pozostałe funkcje aplikacji są ograniczone. Dostępny jest tygodniowy plan posiłków, do którego przeciąga się przepisy, oraz lista zakupów generowana na podstawie tego planu. Każdy użytkownik otrzymuje własne dane logowania. Całość działa w jednym kontenerze i pozostaje w stanie spoczynku między żądaniami, dzięki czemu nawet skromny VPS obsługuje aplikację bez zauważalnego obciążenia.

Niniejszy przewodnik wykorzystuje Docker Compose. Jeśli pojęcia services: oraz volumes: są nieznane, należy najpierw zapoznać się z budową plików Docker Compose, ponieważ poniższa konfiguracja składa się z jednego pliku compose oraz czterech poleceń.

Instalacja Mealie przy użyciu Docker Compose

Mealie publikuje swoje obrazy w rejestrze kontenerów GitHub. Według stanu na październik 2026 bieżąca stabilna wersja to v3.28.0. Należy przypiąć wersję zamiast używać latest: przy latest, docker compose pull w przypadkowym dniu może wymusić migrację bazy danych, na którą użytkownik nie jest przygotowany.

sudo mkdir -p /srv/mealie
cd /srv/mealie
sudo nano docker-compose.yml
services:
  mealie:
    image: ghcr.io/mealie-recipes/mealie:v3.28.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:

Dwie linie wymagają uwagi przed uruchomieniem.

Port zapisano jako 127.0.0.1:9925:9000, a nie 9925:9000. Kontener nasłuchuje wewnątrz na porcie 9000, a host mapuje na niego port 9925. Powiązanie tego mapowania z adresem loopback sprawia, że nginx może połączyć się z Mealie, natomiast Internet nie ma takiej możliwości. Docker zapisuje własne reguły w filtrze pakietów, więc zwykły 9925:9000 jest osiągalny z zewnątrz, nawet jeśli firewall wskazuje, że port jest zamknięty. Warto raz zrozumieć to zjawisko: zobacz dlaczego opublikowane porty Docker ignorują ufw.

BASE_URL musi być dokładnym adresem publicznym, który będzie używany, wraz ze schematem i bez ukośnika na końcu. Mealie buduje na jego podstawie linki do resetowania hasła oraz zaproszenia. Ustawienie http://localhost:9925 spowoduje, że zaproszenie wysłane do partnera będzie zawierać link działający wyłącznie na samym serwerze.

Uruchom usługę i obserwuj pierwsze uruchomienie.

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

Pierwsze uruchomienie tworzy bazę danych SQLite i wykonuje migracje, co trwa kilka sekund. Gdy logi się ustabilizują i przestaną wyświetlać linie migracji, sprawdź aplikację lokalnie.

curl -I http://127.0.0.1:9925

Kod 200 OK oznacza, że aplikacja działa. Connection refused oznacza, że kontener nie jest uruchomiony: wykonaj sudo docker compose ps i odczytaj kod wyjścia. Kontener, który zatrzymał się z kodem 137, został zabity z powodu przekroczenia limitu pamięci 1000M, co zdarza się w najmniejszych planach.

Pierwsze logowanie i wyłączenie otwartej rejestracji

Domyślne konto to changeme@example.com z hasłem MyPassword. Zaloguj się przy jego użyciu, a następnie natychmiast zmień oba te parametry, ponieważ ta para jest podana w dokumentacji i znajduje się w bazie każdego skanera.

Ustawienie ALLOW_SIGNUP: "false" w pliku compose jest celowe. Przy otwartej rejestracji każda osoba, która znajdzie adres, może utworzyć konto w Twoim zbiorze przepisów. Gdy rejestracja jest wyłączona, dodajesz użytkowników z poziomu panelu administratora, co generuje link zaproszenia, który przesyłasz samodzielnie. Link ten jest tworzony na podstawie BASE_URL, dlatego ta wartość jest istotna. Jeśli uruchamiasz kilka aplikacji na tym samym serwerze i chcesz używać jednego hasła do wszystkich, Mealie może przekazać proces logowania do zewnętrznego dostawcy tożsamości, takiego jak samodzielnie hostowana instancja Authentik.

Mealie przypisuje użytkowników do gospodarstwa domowego, a gospodarstwa domowe znajdują się wewnątrz grupy. Wszyscy członkowie jednego gospodarstwa domowego współdzielą plan posiłków i listę zakupów, co jest rozwiązaniem pożądanym w rodzinie. Kolekcja przepisów jest szersza: gospodarstwa domowe współdzielą swoje przepisy, tagi i kategorie z każdym innym gospodarstwem domowym w tej samej grupie. Oddzielne gospodarstwa domowe na jednym serwerze zachowują zatem osobne plany i listy, ale korzystają z tych samych przepisów, co sprawdza się w mieszkaniu studenckim, gdzie domownicy nie zgadzają się co do dodatku anchois. Jeśli użytkownicy potrzebują całkowicie odseparowanych kolekcji, należy przypisać ich do osobnych grup, ponieważ między grupami nie są współdzielone żadne dane.

Importer, czyli powód uruchomienia aplikacji

Otwórz kolekcję przepisów, wybierz opcję tworzenia przepisu z adresu URL i wklej link. Mealie pobiera stronę i wyszukuje ustrukturyzowane dane przepisu, czyli blok czytelny dla maszyn, który większość stron kulinarnych osadza na potrzeby wyszukiwarek. Jeśli ten blok jest obecny, import przebiega czysto i natychmiastowo.

Jeśli skonfigurujesz dostawcę AI, możesz również importować przepisy z obrazu lub wklejonego tekstu, co pozwala na obsługę zdjęć stron z książek kucharskich. Import obrazów wymaga dostawcy zdolnego do ich odczytu. Bez skonfigurowanego dostawcy te metody importu są niedostępne. Przetwarzanie odbywa się wolniejszą ścieżką i wymaga późniejszej weryfikacji, ponieważ odręcznie zapisane ułamki są łatwe do błędnego odczytania.

Import masowy uruchamia się z tego samego ekranu: wklej listę adresów, po jednym w każdej linii, a Mealie przetworzy je w tle. Kolekcja dwustu zakładek może zostać przeniesiona w jednej sesji.

Plany posiłków i lista zakupów

Planer posiłków to kalendarz. Przeciągnij przepis na wybrany dzień, aby go zaplanować. Lista zakupów automatycznie zbiera składniki z zaplanowanych przepisów w jeden zestaw i łączy duplikaty, dzięki czemu dwa przepisy wymagające cebuli generują jedną pozycję zamiast dwóch.

Lista jest aktywną stroną dostępną na telefonie podczas zakupów. Ponieważ dane znajdują się na własnym serwerze, wszyscy domownicy widzą tę samą listę w tym samym czasie, a odznaczenie mleka przez jedną osobę powoduje jego usunięcie z ekranu pozostałych użytkowników.

Umieszczenie nginx i TLS przed aplikacją

Mealie obsługuje wyłącznie zwykły protokół HTTP i nie posiada własnych mechanizmów obsługi certyfikatów. Należy zakończyć szyfrowanie TLS w nginx przed aplikacją. W pierwszej kolejności należy skierować rekord DNS typu A na serwer, ponieważ proces uzyskiwania certyfikatu weryfikuje tę nazwę.

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

nginx -t wyświetla syntax is ok oraz test is successful, co stanowi zabezpieczenie. Przeładowanie konfiguracji należy wykonać dopiero po pomyślnej weryfikacji, ponieważ przeładowanie błędnej konfiguracji pozostawia działającą poprzednią wersję i ukrywa błąd aż do następnego restartu.

client_max_body_size 64M jest wymagane, ponieważ domyślna wartość w nginx wynosi 1 MB. Przesyłanie zdjęcia przepisu lub przywracanie kopii zapasowej przez przeglądarkę wysyła większy obiekt, a bez tej linii otrzymasz 413 Request Entity Too Large z poziomu nginx, a nie z Mealie, przez co dziennik aplikacji nie wykaże żadnego zdarzenia.

Następnie należy wydać certyfikat. Ten krok oraz harmonogram jego odnawiania zostały opisane w wydawanie certyfikatu Let's Encrypt dla nginx przy użyciu certbot.

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

Certbot modyfikuje blok serwera, aby nasłuchiwał na porcie 443 i dodaje przekierowanie z portu 80. Należy załadować stronę przez https:// i potwierdzić, że przeglądarka akceptuje certyfikat. Jeśli Mealie się ładuje, ale własne odnośniki kierują na http://, oznacza to, że BASE_URL nadal zawiera http i wymaga poprawy, po której należy wykonać sudo docker compose up -d w celu odtworzenia kontenera z nową wartością.

Udostępnianie Mealie w podkatalogu, takim jak example.com/recipes, nie działa, ponieważ frontend nie może być serwowany z poziomu podkatalogu. Należy użyć subdomeny.

Kopie zapasowe i rzeczywiste działanie przywracania

Wszystkie dane Mealie znajdują się w /app/data/ wewnątrz kontenera, co stanowi wolumen mealie-data. Skopiowanie tego wolumenu oznacza skopiowanie przepisów, obrazów oraz bazy danych jednocześnie. Dokumentacja Mealie wskazuje zatrzymanie kontenera i skopiowanie tego folderu jako najlepszą metodę tworzenia kopii zapasowej instalacji opartej na SQLite.

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

Nazwa wolumenu zawiera prefiks nazwy projektu, a nazwą projektu jest katalog zawierający plik compose. Z poziomu /srv/mealie wolumenem jest mealie_mealie-data, dlatego pierwszym poleceniem jest docker volume ls: należy użyć nazwy wyświetlonej przez system, a nie nazwy z tego przewodnika. Zatrzymanie kontenera jest istotne, ponieważ SQLite często wykonuje zapisy, przez co kopia wykonana w trakcie pracy może okazać się niemożliwa do odczytania.

Mealie posiada również własną stronę kopii zapasowych w panelu administratora, która zapisuje archiwum zip zawierające bazę danych jako database.json. Nie należy polegać na tej metodzie przy zmianie wersji oprogramowania: archiwum musi zostać przywrócone przez tę samą wersję Mealie, w której zostało utworzone, a niedopasowanie wersji spowoduje błąd. Aby przenieść dane między serwerami, należy uruchomić tę samą przypiętą wersję na nowej maszynie, a następnie skopiować wolumen lub przywrócić archiwum. Przywracanie kopii jest z założenia procesem destrukcyjnym: usuwa bieżącą bazę danych przed załadowaniem archiwum i nie można go cofnąć. Po zakończeniu operacji użytkownik zostaje wylogowany.

Żadna z tych kopii nie stanowi zabezpieczenia, jeśli znajduje się na tym samym serwerze. Archiwum należy regularnie przesyłać w inne miejsce, do czego służą szyfrowane kopie zapasowe poza serwerem z użyciem restic.

Aktualizacja Mealie

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

Zwiększ przypisaną wersję w pliku, a następnie pobierz obraz i odtwórz kontener. Migracje uruchamiają się przy pierwszym starcie nowego obrazu. Przed zmianą wersji głównej wykonaj kopię zapasową wolumenu, ponieważ nieudana migracja może pozostawić bazę danych w stanie nieobsługiwanym przez poprzednią wersję obrazu. Zapoznaj się z informacjami o wydaniu dla wszystkich wersji pomiędzy obecnie używaną a docelową.

Gdy importer zawodzi

Niektóre witryny nie publikują żadnych ustrukturyzowanych danych przepisów, przez co Mealie importuje jedynie tytuł z pustą listą składników. Tego problemu nie da się rozwiązać poprzez konfigurację. W takim przypadku należy ręcznie wkleić tekst przepisu.

Inne błędy wynikają z mechanizmów ochrony przed botami, które znajdują się przed witryną z przepisami. Zamiast treści przepisu, Mealie otrzymuje stronę z wyzwaniem (challenge). Mealie podszywa się pod przeglądarkę i rotuje swój user agent, aby ograniczyć to zjawisko. Jeśli witryna nadal odmawia dostępu, udokumentowane opcje obejmują skierowanie scrapera przez proxy o lepszej reputacji adresu IP lub uruchomienie instancji FlareSolverr, która rozwiązuje wyzwanie w rzeczywistej przeglądarce. Obie opcje są nieobowiązkowe i konfiguruje się je za pomocą zmiennych środowiskowych kontenera.

Import, który kończy się niepowodzeniem z powodu braku łączności serwera z witryną, jest odrębnym problemem. Przed przypisaniem winy scraperowi, należy przetestować połączenie z poziomu serwera za pomocą curl -I https://the-site.example/recipe i sprawdzić kod stanu w odpowiedzi.

Miejsce w infrastrukturze

Mealie to dobra pierwsza aplikacja do samodzielnego hostowania w gospodarstwie domowym, ponieważ domownicy będą z niej korzystać bez dodatkowej zachęty. Jest to zadanie o podobnym charakterze co prowadzenie własnej biblioteki zdjęć za pomocą Immich, choć znacznie mniej obciążające. Aplikacja ta wpisuje się w szerszą listę rzeczy, które warto hostować samodzielnie w tym roku. Jeden niewielki serwer wystarczy do obsługi obu tych rozwiązań. Immich nie jest jedynym kandydatem do tego drugiego zadania, a jeśli decyzja jeszcze nie zapadła, minimalne wymagania pamięciowe oraz polecenia kopii zapasowych dla PhotoPrism i Immich różnią się na tyle, że warto się z nimi zapoznać przed zajęciem pozostałej części dysku. Jeśli domownicy przechowują w jednym miejscu nie tylko plany posiłków, ale także notatki, samodzielnie hostowana przestrzeń robocza AFFiNE stanowi powtórzenie tego samego ćwiczenia z plikiem Compose. Wymaga ona jednak czterech kontenerów i znacznie większej ilości pamięci niż Mealie, więc najpierw należy sprawdzić dostępne zasoby serwera. Jeśli domownicy chcą śledzić swoje treningi obok planów posiłków, openGym przechowuje dane o ćwiczeniach i wymaga tego samego przypiętego tagu, pliku Compose oraz certyfikatu, które zostały już skonfigurowane. Wieczory po kolacji mają własną propozycję, ponieważ Halcyon przekształca istniejącą bibliotekę Jellyfin w wirtualną wypożyczalnię kaset z lat 90.. Jest to kolejny niewielki kontener działający przed już uruchomioną usługą, a nie kolejna baza danych wymagająca kopii zapasowej. Nie wszystko w tej rodzinie rozwiązań to aplikacje domowe. Jeśli ten sam nawyk miałby zostać wykorzystany w pracy, samodzielnie hostowane biuro obsługi klienta Chatwoot wymaga tego samego przypiętego tagu i certyfikatu, ale dodatkowo opiera się na Postgres, Redis oraz działającej poczcie wychodzącej. Jest to znacznie cięższy komponent niż książka kucharska i zasługuje na własny serwer.

FAQ

Dlaczego importowanie przepisu z adresu URL kończy się niepowodzeniem?

Istnieją dwie częste przyczyny. Albo strona nie publikuje ustrukturyzowanych danych przepisu, przez co skraper nic nie znajduje i otrzymujesz tytuł bez składników, albo warstwa ochrony przed botami przed stroną zwraca stronę z wyzwaniem zamiast przepisu. W drugim przypadku Mealie można skierować na proxy z lepszą reputacją adresu lub na samodzielnie hostowaną instancję FlareSolverr, która rozwiązuje wyzwanie w prawdziwej przeglądarce. Przed wprowadzeniem jakichkolwiek zmian potwierdź, że serwer w ogóle może połączyć się ze stroną za pomocą curl -I.

Czy potrzebuję PostgreSQL, czy wystarczy SQLite?

SQLite wystarcza do użytku domowego i jest ustawieniem domyślnym. Przejdź na PostgreSQL, gdy katalog danych znajduje się na sieciowej pamięci masowej (NAS), ponieważ SQLite przez system plików sieciowych generuje błędy blokady bazy danych i może uszkodzić plik. Przywracanie kopii zapasowej w PostgreSQL wymaga, aby użytkownik bazy danych był superużytkownikiem, ponieważ proces przywracania usuwa wszystko przed załadowaniem archiwum.

Czy mogę uruchomić Mealie bez nazwy domeny?

Tak, w sieci lokalnej. Ustaw BASE_URL na adres, który faktycznie będziesz wpisywać, na przykład http://192.168.1.20:9925, i pomiń nginx. Linki do zaproszeń i resetowania hasła są tworzone na podstawie BASE_URL, więc błędna wartość spowoduje wygenerowanie linków, których nikt inny nie będzie mógł otworzyć. Nie wystawiaj usługi do Internetu przez zwykłe HTTP, ponieważ dane logowania będą przesyłane otwartym tekstem.

Jak nadać członkom rodziny własne loginy?

Pozostaw ALLOW_SIGNUP ustawione na "false" i dodaj osoby z poziomu panelu administratora, co wygeneruje link z zaproszeniem do wysłania. Umieść wszystkich domowników korzystających z tej samej kuchni w tym samym gospodarstwie domowym, aby współdzielili plan posiłków i listę zakupów. Przepisy są udostępniane każdemu gospodarstwu domowemu w grupie, więc osoby potrzebujące oddzielnych kolekcji muszą należeć do oddzielnych grup.

Co stanie się z moimi przepisami, jeśli przestanę używać Mealie?

Będą dostępne. Kopia zapasowa administratora zapisuje dane w formacie JSON, a Mealie potrafi również eksportować przepisy jako zwykłe pliki markdown, które pozostają czytelne w każdym edytorze tekstu bez konieczności używania jakiegokolwiek oprogramowania. Wykonaj eksport, zanim będzie potrzebny, i sprawdź, czy możesz go otworzyć.