Mealie自托管教程:VPS上用Docker Compose部署
在VPS上用Docker Compose部署Mealie,固定稳定版本并配置nginx、TLS和备份。粘贴食谱链接即可提取干净步骤,还能生成膳食计划与购物清单。
自托管食谱管理器的作用
自托管食谱管理器会将食谱保存到您拥有的服务器上的数据库中,而 Mealie 是大多数家庭最终会选择的工具。您粘贴食谱网页的地址后,Mealie 会读取其中的食材、步骤、份量和烹饪时间,然后去除故事内容和广告。最终保存到收藏中的只有食物信息。
应用的其余部分很简单。您可以将食谱拖入每周膳食计划,还可以根据该计划生成购物清单。每位下厨的人都可以使用自己的登录账号。整个应用运行在一个容器中,并且在没有请求时处于空闲状态,因此普通 VPS 即可稳定承载。
本指南使用 Docker Compose。如果您不熟悉 services: 和 volumes:,请先阅读Docker Compose 文件的组成方式,因为下面的内容只涉及一个 compose 文件和四条命令。
使用 Docker Compose 安装 Mealie
Mealie 会将其镜像发布到 GitHub 容器注册表。截至 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 会将自己的规则写入数据包过滤器,因此即使防火墙显示该端口已关闭,普通的 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 会将用户归入一个家庭。一个家庭中的所有人共享菜谱集、膳食计划和购物清单,这符合家庭用户的需求。同一服务器上的不同家庭彼此使用独立的菜谱集;合租者之间如果对凤尾鱼没有共识,这种方式更合适。
导入器,这正是运行它的原因
打开食谱集合,选择从 URL 创建食谱,然后粘贴链接。Mealie 会获取页面,并查找结构化食谱数据,即大多数食谱网站为搜索引擎嵌入的机器可读数据块。存在该数据块时,导入过程干净且立即完成。
您也可以从图像或粘贴的纯文本导入。这包括食谱书页面的照片。这些内容会经过较慢的处理流程,之后需要检查,因为手写的分数很容易被误读。
批量导入也可在同一页面执行:粘贴地址列表,每行一个,Mealie 会在后台逐一处理。一次操作即可导入包含 two hundred 个书签的集合。
膳食计划和购物清单
膳食计划器以日历形式提供。将食谱拖到某一天,即可完成计划。购物清单会收集已计划食谱中的食材,并合并重复项。例如,两个食谱都需要洋葱时,清单中只会显示一行,而不是两行。
购物时,您可以在手机上打开实时更新的清单页面。由于清单由您自己的服务器保存,家庭成员可以同时看到同一份清单。一人勾选牛奶后,牛奶也会从其他人的屏幕上移除。
在前端部署 nginx 和 TLS
Mealie 使用明文 HTTP,不能自行处理证书。应在前端 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:// 访问该站点,并确认浏览器接受该证书。如果 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 的内存最低要求及备份命令,因为两者的差异足以影响您是否需要将磁盘剩余空间分配给其他用途。
FAQ
导入食谱 URL 为什么会失败?
通常有两个原因。页面可能没有发布结构化食谱数据,因此抓取器找不到任何内容,最终只得到标题,没有食材;也可能是网站前面的机器人防护层返回了验证页面,而不是食谱。对于第二种情况,可以让 Mealie 使用地址信誉更好的代理,也可以指向自行托管的 FlareSolverr 实例,由它在真实浏览器中完成验证。在修改任何配置前,先使用 curl -I 确认服务器能够访问该页面。
我需要 PostgreSQL,还是 SQLite 就够了?
对于家庭使用,SQLite 已经足够,并且它是默认选项。当数据目录位于网络附加存储上时,应改用 PostgreSQL,因为 SQLite 通过网络文件系统运行会产生数据库锁定错误,并可能损坏文件。使用 PostgreSQL 恢复数据时,数据库用户必须是超级用户,因为恢复过程会先删除所有内容,再加载存档。
不使用域名也能运行 Mealie 吗?
可以,在您自己的网络中运行即可。将 BASE_URL 设置为您实际输入的地址,例如 http://192.168.1.20:9925,并跳过 nginx。邀请链接和密码重置链接根据 BASE_URL 生成,因此值错误会导致其他人无法打开这些链接。不要通过普通 HTTP 将其暴露到互联网,因为登录信息会以明文传输。
如何为家人提供各自的登录账号?
保持 ALLOW_SIGNUP 为 "false",然后从管理区域添加用户,系统会生成邀请链接,您可以将其发送给家人。将共享同一厨房的所有人加入同一个家庭,这样他们可以共享食谱、用餐计划和购物清单。同一服务器上的不同家庭会保留各自独立的内容集合。
停止运行 Mealie 后,我的食谱会怎样?
食谱仍然可以导出。管理后台的备份功能会将数据写入 JSON,Mealie 还可以将食谱导出为纯 Markdown 文件;这些文件无需任何软件,用文本编辑器即可读取。请在需要之前先完成一次导出,并确认您能够打开导出的文件。