如何用 Docker Compose 在 VPS 自托管 Mealie
在 VPS 上用 Docker Compose 部署 Mealie:粘贴食谱链接自动提取步骤,并配置膳食计划、购物清单、nginx、TLS 与备份。固定版本,避免迁移不兼容。
自托管食谱管理器的作用
自托管食谱管理器会将食谱存储在您拥有的服务器上的数据库中,而 Mealie 是多数家庭最终选择的应用。您粘贴食谱页面的地址,Mealie 会从页面中提取食材、步骤、份量和烹饪时间,并去除故事内容和广告。最终保存到食谱库中的只有菜谱内容。
应用的其余部分很简单。您可以将食谱拖入每周膳食计划,还可以根据该计划生成购物清单。每位下厨的人都可以使用自己的登录账号。整个应用运行在一个容器中,处理完请求后基本处于空闲状态,因此普通 VPS 也能轻松运行。
本指南使用 Docker Compose。如果您不熟悉 services: 和 volumes:,请先阅读Docker Compose 文件的组成方式,因为下面的内容就是一个 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.ymlservices:
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:启动前需要检查下面两行。
端口写成 127.0.0.1:9925:9000,而不是 9925:9000。容器内部监听 9000 端口,主机将 9925 映射到该端口。将此映射绑定到 loopback 地址后,nginx 可以访问 Mealie,而互联网无法访问。Docker 会将自己的规则写入 packet filter,因此即使防火墙显示该端口已关闭,纯 9925:9000 仍可能从外部访问。应了解这一点:参见为什么已发布的 Docker 端口会绕过 ufw。
BASE_URL 必须是你实际使用的完整公网地址,包含 scheme,且末尾不能有斜杠。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 生成,因此这个值很重要。如果您最终在同一台服务器上运行多个应用,并希望所有应用使用同一个密码,Mealie 可以将登录交给外部身份提供商,例如 自托管的 Authentik 实例。
Mealie 将用户划分到 household 中。同一 household 中的所有人共享菜谱集合、用餐计划和购物清单,这适合家庭使用。同一服务器上的不同 household 各自保留独立的集合,适合合租者在没有人同意加入凤尾鱼时使用。
导入器:运行它的原因
打开食谱集合,选择从 URL 创建食谱,然后粘贴链接。Mealie 会获取页面,并查找结构化食谱数据。这是大多数食谱网站为搜索引擎嵌入的机器可读数据块。存在该数据块时,导入过程干净且立即完成。
您也可以从图片或粘贴的纯文本导入食谱。这包括食谱书页面的照片。这些内容会经过较慢的处理流程,之后需要检查,因为手写分数很容易被误读。
批量导入也可在同一页面执行:粘贴地址列表,每行一个,Mealie 会在后台逐个处理。一次即可导入包含 two hundred 个书签的集合。
膳食计划和购物清单
膳食计划器以日历形式显示。将食谱拖到某一天,即可完成计划。购物清单会收集已计划食谱中的食材,并合并重复项。因此,两个食谱都需要洋葱时,清单中只会显示一行,而不是两行。
购物时,购物清单会在手机上实时显示。由于清单由您自己的服务器保存,家庭成员可以同时看到同一份清单。一人勾选牛奶后,牛奶也会从其他人的屏幕上移除。
在前端配置 nginx 和 TLS
Mealie 使用纯 HTTP,不负责处理证书。在 Mealie 前端的 nginx 中终止传输层安全协议(TLS)。先将 DNS A 记录指向服务器,因为证书申请过程会验证该域名。
sudo apt update && sudo apt install -y nginx
sudo nano /etc/nginx/sites-available/mealieserver {
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 nginxnginx -t 输出 syntax is ok 和 test is successful 是放行条件。只有检查通过后才能重新加载配置,因为重新加载损坏的配置会继续使用旧配置,并将错误隐藏到下次重启。
配置 client_max_body_size 64M 是因为 nginx 的默认值为 1 MB。通过浏览器上传菜谱照片或恢复备份时,请求正文可能超过该大小。没有这一行时,nginx 会返回 413 Request Entity Too Large,而不是由 Mealie 返回,因此应用日志中完全不会记录相关信息。
然后申请证书。相关步骤及其续期计时器请参阅使用 certbot 为 nginx 申请 Let's Encrypt 证书。
sudo apt install -y certbot python3-certbot-nginx
sudo certbot --nginx -d recipes.example.comCertbot 会重写 server block,使其监听 443 端口,并添加从 80 端口到 HTTPS 的重定向。通过 https:// 访问该站点,并确认浏览器接受该证书。如果 Mealie 可以加载,但其中的链接将您跳转到 http://,说明 BASE_URL 仍设置为 http。请修正该值,然后执行 sudo docker compose up -d,以使用新值重新创建容器。
不要将 Mealie 部署在 example.com/recipes 等子路径下,因为前端无法从子路径提供服务。请使用子域名。
备份,以及还原实际执行的操作
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提高文件中固定的版本号,然后拉取镜像并重新创建容器。新镜像首次启动时会执行迁移。在升级主版本前备份卷,因为迁移中途失败会留下旧镜像无法再打开的数据库。阅读当前版本与新版本之间所有版本的发布说明。
导入器失败时
有些网站根本不发布结构化食谱数据,因此 Mealie 导入后只有标题,食材列表为空。这不是通过配置就能解决的问题。请改为手动粘贴食谱文本。
其他失败可能是食谱网站前面的机器人防护导致的。该防护会向 Mealie 返回挑战页面,而不是食谱内容。Mealie 已经会模拟浏览器,并轮换 user agent,以减少此类问题。网站仍然拒绝请求时,文档提供的选项是:让抓取器通过地址信誉更好的代理发送请求,或运行 FlareSolverr 实例,在真实浏览器中完成挑战。这两种方式都是可选的,并且都通过容器的环境变量配置。
如果导入失败是因为服务器根本无法访问该网站,则属于另一类问题。请在服务器上使用 curl -I https://the-site.example/recipe 进行测试,并先读取状态行,再判断是否是抓取器导致的问题。
适用场景
Mealie 是家庭用户适合部署的第一个自托管应用,因为与您共同生活的人无需提醒就会使用它。它与运行自己的 Immich 照片库属于同一类用途,但轻量得多,也属于更广泛的今年值得自托管的服务。一台小型服务器即可同时运行这两者。Immich 并不是第二项用途的唯一选择;如果您仍在决定,PhotoPrism 和 Immich 的内存需求下限及备份命令差异足够明显,值得在用掉剩余磁盘空间前先阅读。如果家庭除了记录晚餐,还要记录计划和笔记,自托管 AFFiNE 工作区同样只需编写 compose 文件,但它需要4个容器,所需内存也比 Mealie 多得多,因此请先检查服务器剩余资源。如果家庭还想记录训练情况,openGym 会保存训练记录,并且同样需要您刚刚配置好的固定标签、compose 文件和证书。晚餐后的活动也有对应的选择:Halcyon 会将现有的 Jellyfin 媒体库重建为一间可以四处走动的1990年代录像带租赁店。它只是在您已经运行的服务前增加一个小型容器,而不是再增加一个需要备份的数据库。这个系列中的服务并不都是家庭应用。如果同样的习惯以后需要支持工作用途,自托管 Chatwoot 客服台同样需要固定标签和证书,并在后端配置 Postgres、Redis 以及可正常工作的出站邮件。与菜谱应用相比,它是负载重得多的租户,应部署在独立服务器上。
FAQ
导入食谱 URL 为什么失败?
通常有两个原因。网页可能没有发布结构化食谱数据,因此抓取程序找不到内容,结果只有标题,没有食材。网站前面也可能部署了反机器人保护层,返回验证页面而不是食谱。对于第二种情况,可以让 Mealie 使用地址信誉更好的代理,或指向自行托管的 FlareSolverr 实例,由真实浏览器完成验证。在进行任何更改前,先使用 curl -I 确认服务器能否访问该页面。
我需要 PostgreSQL,还是 SQLite 就够了?
家庭使用 SQLite 就够了,它也是默认选项。当数据目录位于网络附加存储上时,应改用 PostgreSQL,因为 SQLite 通过网络文件系统运行会产生数据库锁定错误,并可能损坏文件。使用 PostgreSQL 恢复数据时,数据库用户必须是 superuser,因为恢复过程会先删除所有内容,再加载归档文件。
不使用域名也能运行 Mealie 吗?
可以,在您自己的网络中运行即可。将 BASE_URL 设置为您实际输入的地址,例如 http://192.168.1.20:9925,并跳过 nginx。邀请链接和密码重置链接根据 BASE_URL 生成,因此该值错误会导致其他人无法打开链接。不要通过明文 HTTP 将其暴露到互联网,因为登录信息会以明文传输。
如何为家人提供各自的登录账号?
保持 ALLOW_SIGNUP 为 "false",然后从管理区域添加用户,系统会生成邀请链接,您可以将其发送给家人。将共享同一厨房的所有人加入同一个 household,这样他们可以共享食谱、用餐计划和购物清单。同一服务器上的不同 household 会分别维护各自的内容集合。
停止运行 Mealie 后,我的食谱会怎样?
食谱仍然可以导出。管理区域中的备份功能会将数据写入 JSON,Mealie 也可以将食谱导出为纯 markdown 文件。无需任何软件,这些文件也能在文本编辑器中读取。请在需要之前先执行一次导出,并确认您能够打开导出的文件。