SSD Nodes Learn Hosting plans →
ガイド Matt Connor著者 Matt Connor ・更新日 2026-08-28

VPSにMealieを構築する方法 Docker Compose入門

Docker ComposeでVPSにMealieを構築します。レシピURLから材料と手順を取得し、献立、買い物リスト、nginx、TLS、バックアップまで設定できます。

自宅運用のレシピ管理アプリの役割

自宅運用のレシピ管理アプリは、自分で管理するサーバー上のデータベースにレシピを保存します。多くの家庭では Mealie が選ばれています。レシピページの URL を貼り付けると、Mealie はそこから材料、手順、出来上がり量、調理時間を読み取ります。記事本文や広告は取り込みません。コレクションに保存されるのは料理の情報です。

アプリの機能はシンプルです。レシピをドラッグして登録できる週間献立があり、その献立を基に買い物リストを作成できます。料理をする人ごとにログインを用意できます。すべてが 1 つのコンテナで動作し、リクエストがない間はほぼ待機状態になるため、小規模な VPS でも負荷を意識せず運用できます。

このガイドでは Docker Compose を使用します。services: と volumes: が初めての場合は、まず Docker Compose ファイルの構成方法 を読んでください。以下の内容は、1 つの compose ファイルと 4 つのコマンドだけで構成されています。

Docker Compose で Mealie をインストールする

Mealie はイメージを GitHub container registry に公開しています。2026 年 7 月時点で、現在の安定版タグは v3.22.0 です。latest を使用するのではなく、バージョンを固定してください。latest を使用すると、無関係な日に docker compose pull が更新され、準備していないデータベースマイグレーションに移行する可能性があります。

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:

起動する前に、2 行を確認してください。

ポートは 9925:9000 ではなく 127.0.0.1:9925:9000 と記述します。コンテナ内では 9000 番ポートで待ち受け、ホストの 9925 番ポートから転送します。このマッピングを loopback アドレスにバインドすると、nginx からは Mealie に接続できますが、インターネットからは接続できません。Docker はパケットフィルターに独自のルールを書き込むため、ファイアウォールでポートを閉じていても、通常の 9925:9000 は外部から到達可能です。この挙動は一度理解しておく価値があります。公開された Docker ポートが ufw を無視する理由を参照してください。

BASE_URL には、実際に使用する正確な公開アドレスを設定してください。スキームを含め、末尾にスラッシュは付けません。Mealie はこの値からパスワードリセットリンクと招待リンクを生成します。http://localhost:9925 に設定すると、パートナーに送る招待には、そのサーバー上でしか機能しないリンクが含まれます。

起動して、初回起動時の処理を監視します。

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

初回起動では SQLite データベースが作成され、マイグレーションが実行されます。数秒かかります。ログが落ち着き、マイグレーションの行が出力されなくなったら、ローカルでアプリを確認します。

curl -I http://127.0.0.1:9925

200 OK なら、アプリは起動しています。Connection refused なら、コンテナは実行されていません。sudo docker compose ps を実行し、終了コードを確認してください。終了コード 137 で停止したコンテナは、1000M のメモリ上限を超えたため強制終了されています。最小構成のプランでは、この状況が発生します。

初回ログインと公開サインアップの無効化

デフォルトのアカウントは changeme@example.com、パスワードは MyPassword です。これでログインしたら、すぐに両方を変更してください。この組み合わせはドキュメントに記載されているため、すべてのスキャナーに知られています。

compose ファイルの ALLOW_SIGNUP: "false" は意図的な設定です。サインアップを開放すると、アドレスを見つけた人は誰でもレシピボックスにアカウントを作成できます。無効にすると、管理画面からユーザーを追加でき、送信者自身が招待リンクを送る運用になります。このリンクは BASE_URL を基に生成されるため、この値が重要です。同じサーバーで複数のアプリを運用し、すべてで 1 つのパスワードを使いたい場合、Mealie は 自己ホスト型の Authentik インスタンス などの外部 ID プロバイダーにログイン処理を委任できます。

Mealie では、ユーザーを household 単位でグループ化します。同じ household の全員がレシピコレクション、献立、買い物リストを共有します。これは家族での利用に適しています。同じサーバー上で household を分ければ、コレクションも分離されます。誰もアンチョビに同意しないルームシェアでは、この構成が適しています。

このインポーターを実行する理由

レシピコレクションを開き、URL からレシピを作成する操作を選んで、リンクを貼り付けます。Mealie はページを取得し、構造化レシピデータを探します。これは、多くのレシピサイトが検索エンジン向けに埋め込んでいる、機械で読み取れるデータブロックです。このブロックがあれば、インポートは正確かつすぐに完了します。

画像からインポートすることも、貼り付けたプレーンテキストからインポートすることもできます。これにより、料理本のページを撮影した画像にも対応できます。これらは処理に時間がかかるため、後で内容を確認してください。手書きの分数は読み違えやすいためです。

一括インポートも同じ画面から実行できます。アドレスの一覧を1行に1件ずつ貼り付けると、Mealie がバックグラウンドで順番に処理します。ブックマーク200件のコレクションも、1回の操作で移行できます。

食事計画と買い物リスト

食事プランナーはカレンダーです。レシピを日付にドラッグすると、その日に登録されます。買い物リストには、登録したレシピの材料が 1 つのリストとして集約され、重複も統合されます。そのため、2 つのレシピで玉ねぎが必要な場合も、2 行ではなく 1 行にまとめられます。

買い物中は、スマートフォンでリストをリアルタイムに確認できます。自分のサーバーで管理するため、家族全員が同じリストを同時に確認できます。1 人が牛乳を購入済みとしてチェックすると、別の人の画面からも牛乳が削除されます。

nginx と TLS を前段に配置する

Mealie は平文の HTTP を使用し、証明書を独自には処理しません。前段の nginx でトランスポート層セキュリティ(TLS)を終端します。まず、サーバーを指す DNS A レコードを設定してください。証明書の発行時に、その名前が検証されるためです。

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 の出力、続いて syntax is ok と test is successful の出力を確認することが重要です。検証に成功してから reload してください。壊れた設定を reload すると、古い設定のまま動作し続けるため、次回の再起動まで問題が隠れてしまいます。

nginx のデフォルト値は 1 MB なので、client_max_body_size 64M が必要です。レシピの写真をアップロードしたり、ブラウザーからバックアップを復元したりすると、これを超えるサイズの body が送信されます。この行がない場合、nginx から 413 Request Entity Too Large が返されます。Mealie が返すエラーではないため、アプリケーションログには何も記録されません。

続いて証明書を発行します。その手順と更新用 timer については、certbot で nginx 用の Let's Encrypt 証明書を発行するで説明しています。

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

Certbot は server block を 443 番ポートで待ち受けるように書き換え、80 番ポートからの redirect も追加します。https:// でサイトを開き、ブラウザーが証明書を受け入れることを確認してください。Mealie は表示されるものの、内部リンクで http:// に移動する場合は、BASE_URL がまだ http のままです。これを修正した後、sudo docker compose up -d を実行して新しい値でコンテナを再作成してください。

example.com/recipes のようなサブパスで Mealie を提供することはできません。フロントエンドがサブパスから配信できないためです。サブドメインを使用してください。

バックアップと、リストアで実際に行われること

Mealie が管理するすべてのデータは、コンテナ内の /app/data/ に保存されます。これは mealie-data ボリュームです。このボリュームをコピーすれば、レシピ、画像、データベースをまとめてコピーできます。

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

ボリューム名にはプロジェクト名がプレフィックスとして含まれます。プロジェクト名は compose ファイルを格納しているディレクトリ名です。/srv/mealie では、ボリュームは mealie_mealie-data です。そのため、最初のコマンドは docker volume ls になります。このガイドに記載された名前ではなく、コマンドが表示した名前を使用してください。先にコンテナを停止することが重要です。SQLite は書き込み中であることが多く、実行中のコピーからは読み取れない状態でリストアされる可能性があります。

Mealie には管理画面にも独自のバックアップページがあります。この機能では、データベースを JSON として格納し、画像も含めた移行可能なアーカイブを作成します。サーバー間の移行にはこの機能を使用してください。単純なファイルコピーと異なり、バージョン変更後でも利用できるためです。アーカイブからのリストアは、意図的に破壊的な処理です。現在のデータベースを削除してからアーカイブを読み込むため、元に戻せません。処理が完了するとログアウトされます。

同じサーバー上に置いている間は、どちらのコピーもバックアップとはいえません。アーカイブをスケジュールに従って別の場所へ送信してください。restic を使用した暗号化オフサーバーバックアップは、そのための機能です。

Mealie の更新

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

ファイル内の固定バージョンを引き上げてから、イメージを pull してコンテナを再作成します。新しいイメージで最初に起動するときに、マイグレーションが実行されます。メジャーバージョンを変更する前に、ボリュームのコピーを作成してください。マイグレーションが途中で失敗すると、以前のイメージでは開けなくなるデータベースが残るためです。現在のバージョンから新しいバージョンまでのすべてのリリースノートを確認してください。

インポーターが失敗する場合

構造化されたレシピデータをまったく公開していないサイトもあります。その場合、Mealie は材料一覧が空のままタイトルをインポートします。これは設定で回避できません。代わりに、レシピ本文を手動で貼り付けてください。

その他の失敗は、レシピサイトの前段にあるボット対策が原因です。サイトがレシピではなくチャレンジページを Mealie に返すためです。Mealie はすでにブラウザーを偽装し、これを軽減するために User-Agent をローテーションしています。それでもサイトが拒否する場合、文書化されている方法は、評価の高い IP アドレスを持つプロキシ経由でスクレイパーを実行するか、実際のブラウザーでチャレンジを解決する FlareSolverr インスタンスを実行することです。どちらも任意で、コンテナの環境変数で設定します。

サーバーがサイトにまったく接続できないためにインポートが失敗する場合は、別の問題です。curl -I https://the-site.example/recipe でサーバー上からテストし、スクレイパーを疑う前にステータス行を確認してください。

適用範囲

Mealie は、家庭向けの最初のセルフホストアプリとして適しています。同居する人に頼まなくても使ってもらえるからです。これは Immich で自分の写真ライブラリを運用することと同じ種類の用途ですが、はるかに軽量です。また、今年セルフホストする価値があるものの一覧にも含まれます。小規模なサーバー1台で両方を運用できます。2つ目の用途に使える候補は Immich だけではありません。まだ選定中であれば、PhotoPrism と Immich のメモリ要件とバックアップコマンドには読む価値があります。ディスクの残りを使い切る前に、両者の違いを確認してください。家庭で夕食だけでなく予定やメモも管理するなら、セルフホスト型 AFFiNE ワークスペースも同じ compose ファイルを使う構成になります。ただし、必要なコンテナは4つで、Mealie よりもはるかに多くのメモリを要求します。まずサーバーに残っているリソースを確認してください。家庭で夕食だけでなくトレーニングも記録するなら、openGym でワークアウトを管理する構成も利用できます。直前に設定したものと同じ pinned tag、compose ファイル、証明書を使用します。夕食後の時間には、別の用途もあります。Halcyon は既存の Jellyfin ライブラリを歩き回れる1990年代のレンタルショップとして再構成します。これは、すでに運用しているサービスの前段に追加する小規模なコンテナであり、バックアップ対象となる別のデータベースではありません。この分野にあるものがすべて家庭向けアプリとは限りません。同じ運用方法を仕事にも使う必要が生じた場合は、セルフホスト型 Chatwoot サポートデスクを利用できます。これも同じ pinned tag と証明書を使いますが、背後で Postgres、Redis、動作する外向きメールを必要とします。レシピ管理よりもはるかに重いテナントなので、専用サーバーを用意する価値があります。

FAQ

レシピ URL のインポートに失敗するのはなぜですか?

主な原因は 2 つあります。ページが構造化されたレシピデータを公開していないため、スクレイパーが何も検出できず、材料のないタイトルだけが取得される場合があります。または、サイトの前段にあるボット対策が、レシピの代わりにチャレンジページを返している場合があります。後者では、Mealie の接続先を、アドレスの評価が高いプロキシ、または実際のブラウザーでチャレンジを解決する self-hosted FlareSolverr インスタンスに変更できます。変更する前に、curl -I でサーバーからページへ到達できることを確認してください。

PostgreSQL は必要ですか。それとも SQLite で十分ですか?

家庭で使う場合は SQLite で十分です。SQLite がデフォルトです。データディレクトリをネットワーク接続ストレージに置く場合は PostgreSQL に移行してください。ネットワークファイルシステム上の SQLite は、データベースのロックエラーを発生させたり、ファイルを破損させたりするためです。PostgreSQL でリストアするには、データベースユーザーが superuser である必要があります。リストアではアーカイブを読み込む前にすべてのデータを削除するためです。

ドメイン名なしで Mealie を実行できますか?

自分のネットワーク内であれば実行できます。BASE_URL に、実際に入力するアドレスを設定してください。たとえば http://192.168.1.20:9925 のようにします。nginx は使用しません。招待リンクとパスワードリセットリンクは BASE_URL から生成されるため、値を誤ると他のユーザーが開けないリンクになります。ログイン情報が平文で送信されるため、plain HTTP でインターネットに公開しないでください。

家族にそれぞれのログインを割り当てるにはどうすればよいですか?

ALLOW_SIGNUP を "false" のままにし、管理画面からユーザーを追加してください。招待リンクが生成されるので、そのリンクを家族に送信します。同じキッチンを使うユーザーは全員、同じ household に追加してください。レシピ、献立、買い物リストを共有できます。1 台のサーバー上で household を分けると、コレクションも分離されます。

Mealie の実行を停止すると、レシピはどうなりますか?

取り出せます。管理画面のバックアップ機能を使うと、データが JSON として保存されます。Mealie では、レシピをプレーンな markdown ファイルとしてエクスポートすることもできます。これらはソフトウェアがなくても、任意のテキストエディターで読み取れます。必要になる前に 1 回エクスポートし、開けることを確認してください。