Recipya Docker Compose 部署指南
Recipya v1.2.2(reaper47/recipya,Go 版)官方 Docker 路径部署实测:单容器+SQLite 形态纠偏、v 前缀 tag 与 latest≠v1.2.2、镜像白名单绕行、8163:8078 映射、FDC 营养库首启自下载、注册→建食谱→URL 导入(WebSocket 前置坑)→Cookbook(HX-Prompt)全闭环与验收清单。
Recipya Docker Compose 部署指南
目标:在真实 Linux 宿主上用官方 Docker 路径部署 Recipya v1.2.2(Go 版)单容器实例,完成首次登录、手动建食谱、URL 抓取导入的端到端闭环。全部命令与回显取自 2026-11-02 真实环境实测(完整取证记录:
/opt/data/quzuo/tmp/recipya/evidence.md)。⚠️ 验收方式说明:本实验环境无浏览器自动化,页面验收降级为 HTTP 状态码 + 正文断言 + API 闭环验证(curl/python 调用的正是前端页面同组使用的 HTMX 路由,服务端行为等价)。主题切换、拖拽排序等纯浏览器交互未做 UI 级验收。
一、环境信息
- 宿主:Linux x86_64
- Docker:
29.9.2(Docker version 29.9.2, build a82e528) - Docker Compose:
v5.6.1(docker compose version --short→5.6.1) - Recipya:镜像 tag v1.2.2(GitHub
releases/latest实查的最新稳定版,2024-12-20 发布) - 部署目录:
/opt/data/quzuo/tmp/recipya/ - 端口:宿主 8163 → 容器 8078(8162 已被同环境 RecipeSage 代理占用,实测改选 8163)
端口规划(本实验环境实测占用情况)
8128/8133/8147 IDE 进程占用
8149 gancio-1.28.2
8152/8157 mealie-3.30.0
8159 python3
8162 recipesage_proxy(勿用)
8163 ← 本部署选用
部署前先自查:ss -tln | grep 8163 或 (echo >/dev/tcp/127.0.0.1/8163) 2>/dev/null && echo BUSY || echo FREE。
二、纠偏须知(部署前必读)
- 上游仓库是
reaper47/recipya,不是某些清单里写的 fernandoracane/Recipya(GitHub API 实测 404)。Docker Hub 镜像为reaper99/recipya。 - 版本 tag 带
v前缀:reaper99/recipya:v1.2.2存在,1.2.2(不带 v)不存在——实测docker pull docker.1ms.run/reaper99/recipya:1.2.1返回not found。 - Go 版仅支持 SQLite:
main.go硬编码NewSQLiteService(),仓库内无任何 postgres 代码。不存在"PostgreSQL 形态",那是 Rust 重写版(recipya-rs)的能力。 - EXPOSE 8080 是误导:镜像
ExposedPorts写 8080,但应用实际监听端口由配置决定(默认RECIPYA_SERVER_PORT=8078),端口映射必须对 8078。 - 国内镜像加速环境可能遇到 DaoCloud 白名单拦截(见 §3.2 实测报错)。
三、获取镜像
3.1 标准路径
docker pull reaper99/recipya:v1.2.2
3.2 镜像加速环境实测(纠偏记录)
本机 daemon 配置了 registry-mirrors(docker.m.daocloud.io / docker.1ms.run / hub.rat.dev)。直连拉取失败:
Error response from daemon: error from registry: 🚫 👀-> https://github.com/DaoCloud/public-image-mirror/issues/2328 🔗 这镜像不在白名单. this image is not in the allowlist.
改用显式镜像源成功:
docker pull docker.1ms.run/reaper99/recipya:v1.2.2
# 拉取成功后重打官方名,保持 compose 与文档一致
docker tag docker.1ms.run/reaper99/recipya:v1.2.2 reaper99/recipya:v1.2.2
docker rmi docker.1ms.run/reaper99/recipya:v1.2.2
实测镜像清单:
reaper99/recipya:v1.2.2 7f7ec3aeb88c 273MB
docker.1ms.run/...:latest 61a5a88fce48 240MB # latest ≠ v1.2.2(镜像 ID 不同)
docker.1ms.run/...:nightly bad140a7ff6e 263MB
注意 latest 与 v1.2.2 不是同一镜像,生产部署务必钉死 v1.2.2(或按需选 nightly,见选型篇的 Rust 迁移背景)。
四、compose.yaml
官方模板在主仓库 deploy/compose.yaml(nightly 版)。以下为本实测文件 /opt/data/quzuo/tmp/recipya/compose.yaml(钉版本 + 端口 8163 + 全量环境变量显式声明):
services:
recipya:
image: reaper99/recipya:v1.2.2
container_name: recipya-1.2.2-evidence
restart: unless-stopped
environment:
RECIPYA_EMAIL: "" # SendGrid 发信账号,留空则不发邮件
RECIPYA_EMAIL_SENDGRID: "" # SendGrid API key
RECIPYA_DI_KEY: "" # Azure 文档智能 KEY 1(OCR 用)
RECIPYA_DI_ENDPOINT: "" # Azure 文档智能 Endpoint
RECIPYA_SERVER_AUTOLOGIN: "false" # 单用户免登录模式
RECIPYA_SERVER_BYPASS_GUIDE: "false" # 跳过内置使用向导
RECIPYA_SERVER_IS_DEMO: "false"
RECIPYA_SERVER_IS_PROD: "false" # 公网暴露应设 true(影响 cookie secure)
RECIPYA_SERVER_NO_SIGNUPS: "false" # 建好账号后建议改 true
RECIPYA_SERVER_PORT: 8078 # 必填:应用监听端口
RECIPYA_SERVER_URL: "http://0.0.0.0"
ports:
- "8163:8078"
volumes:
- "recipya-data:/root/.config/Recipya"
volumes:
recipya-data:
要点:
- 唯一持久化卷是
/root/.config/Recipya(数据库、图片、备份、日志、config.json 全在里面),丢了它等于丢全部数据。 - 环境变量全集与语义见 《Recipya v1.2.2 配置参数详解》。
五、启动与首验
cd /opt/data/quzuo/tmp/recipya
docker compose up -d
实测输出:
Network recipya_default Created
Container recipya-1.2.2-evidence Started
docker ps --filter name=recipya
# recipya-1.2.2-evidence Up 8080/tcp, 0.0.0.0:8163->8078/tcp
首次启动日志(注意 FDC 营养库自动下载与 goose 迁移):
Fetching the FDC database (62.6 MB)
Environment variables ok
OK FFmpeg is installed
Recipya is properly set up
File locations:
- Backups: /root/.config/Recipya/Backup
- Database: /root/.config/Recipya/Database
- Images: /root/.config/Recipya/Images
- Logs: /root/.config/Recipya/Logs
- Videos: /root/.config/Recipya/Videos
2026/11/01 21:07:33 OK 20221227180503_init.sql (389.62µs)
...
2026/11/01 21:07:33 OK 20241219030306_refresh_scraper.sql
- FDC 数据库(美国农业部食物成分库)首次启动自动拉取,落盘
fdc.db约 200 MB——离线环境需预置该文件,否则营养计算功能不可用。 - 21 个 goose 迁移自动执行,无需手工初始化。
HTTP 首验:
curl -s -o /dev/null -w '%{http_code} %{redirect_url}\n' http://127.0.0.1:8163/
# 303 http://127.0.0.1:8163/guide/en ← 匿名访问跳转内置使用向导(bypassGuide=false)
浏览器打开 http://<宿主IP>:8163 同样先落到 /guide/en 向导页;不想看向导设 RECIPYA_SERVER_BYPASS_GUIDE=true(或按配置篇改用 config.json)。
六、首次登录与建食谱闭环(实测命令)
Recipya 无"默认管理员":第一个通过注册页创建的账号即 admin(实测注册后
GET /admin→ 200)。未配置 SendGrid 时注册确认邮件只入队不发送,不阻塞登录。
6.1 注册 + 登录
cd /tmp
curl -s -c cj.txt -o /dev/null -w '%{http_code}\n' http://127.0.0.1:8163/auth/register # 200
curl -s -b cj.txt -c cj.txt -o /dev/null -w '%{http_code}\n' \
-X POST \
--data-urlencode 'email=admin@recipya.test' \
--data-urlencode 'password=Recipya#2026' \
--data-urlencode 'password-confirm=Recipya#2026' \
http://127.0.0.1:8163/auth/register
# 303(HX-Redirect: /auth/login)
curl -s -b cj.txt -c cj.txt -D - -o /dev/null \
-X POST --data-urlencode 'email=admin@recipya.test' --data-urlencode 'password=Recipya#2026' \
http://127.0.0.1:8163/auth/login
# 200,Set-Cookie: session=<uuid>; HttpOnly; SameSite=Strict,HX-Redirect: /
表单字段:注册 email/password/password-confirm;登录 email/password,勾选"记住我"对应 remember-me=yes。
6.2 手动创建食谱
curl -s -b cj.txt -D - -o /dev/null \
-F 'title=番茄意面 Test' -F 'category=dinner' -F 'description=取证测试食谱' \
-F 'ingredients=200g 意大利面' -F 'ingredients=3 个番茄' \
-F 'instructions=煮熟意面' -F 'instructions=炒番茄酱拌匀' \
-F 'keywords=意面' -F 'time-preparation=10m' -F 'time-cooking=20m' \
-F 'yield=2' -F 'calories=350' \
http://127.0.0.1:8163/recipes/add/manual
# 201,HX-Redirect: /recipes/1
验证:GET /recipes/1 → 200,正文含"番茄意面";GET '/recipes/search?q=番茄' → 200 命中(SQLite FTS5 中文检索可用)。
6.3 URL 抓取导入(⚠️ 必须先建 WebSocket)
直接 POST 会失败——这是 Recipya API 自动化的头号坑:
curl -s -b cj.txt -X POST -F 'urls=https://www.recipetineats.com/spaghetti-bolognese/' \
http://127.0.0.1:8163/recipes/add/website
# 400,HX-Trigger: ...Connection lost. Please reload page.
源码原因:recipesAddWebsiteHandler 首行检查 s.Brokers.Has(userID)——导入进度/结果通过 /ws WebSocket 推送,该用户必须有活动 WS 连接才受理请求。导出(/settings/export/recipes)与文件导入(/recipes/add/import)同理。
自动化脚本实测(python websocket-client,取证脚本 forensics/ws_import.py):
ws = websocket.WebSocketApp("ws://127.0.0.1:8163/ws", cookie=f"session={sid}", ...)
# 线程中 run_forever,sleep 2s 确认连接后:
requests.post(f"{BASE}/recipes/add/website", files={"urls": (None, url)}, cookies=...)
# 202 Accepted
WS 收到的实测消息序列:
(进度帧)<p ...>Fetching 1/1</p> <progress max="100" value="100.000000">
(隐藏帧)...container hidden...
(toast){"type":"toast","toast":{"action":"View /recipes/2",
"background":"alert-info","message":"Recipe has been added to your collection.",
"title":"Operation Successful"}}
验证:GET /recipes/2 → 200,标题 Spaghetti Bolognese,站点图片已抓取并转为 /data/images/b6e925a7-....webp(25,830 字节)+ Thumbnails 缩略图。
6.4 建菜谱册(Cookbook,HTMX 特性)
cookbook 标题不在表单里,而在 HX-Prompt 请求头(HTMX hx-prompt 弹窗的产物):
curl -s -b cj.txt -X POST -H 'HX-Prompt: 取证测试册' http://127.0.0.1:8163/cookbooks
# 200
curl -s -b cj.txt http://127.0.0.1:8163/cookbooks | grep -c 取证测试册
# 1
用表单字段 name/title 提交只会得到 400 Title must not be empty.(实测踩坑记录)。
七、部署验收清单
| 检查项 | 命令 | 实测结果 |
|---|---|---|
| 容器健康 | docker ps | Up,0.0.0.0:8163->8078/tcp |
| 匿名跳转 | curl -w '%{redirect_url}' http://.../ | 303 → /guide/en(bypassGuide=false 时) |
| 登录页 | curl -o /dev/null -w '%{http_code}' .../auth/login | 200 |
| 注册→登录 | §6.1 | 303 → 200 + session cookie |
| 手动建食谱 | §6.2 | 201 → /recipes/1 |
| URL 导入 | §6.3(带 WS) | 202 → toast → /recipes/2 |
| 数据落卷 | docker exec recipya-1.2.2-evidence ls /root/.config/Recipya | Backup/Database/Images/Logs/Videos |
| SQLite | 同上 ls Database/ | recipya.db(+wal/shm)、fdc.db 200MB |
| 自动备份 | ls Backup/global/ | recipya.2026-11-01.zip(首启即生成) |
| 首个账号即管理员 | curl -b cj.txt .../admin | 200 |
八、日常运维命令
cd /opt/data/quzuo/tmp/recipya
docker compose logs -f recipya # 跟踪日志(应用日志另见卷内 Logs/recipya.log,slog JSON 格式)
docker compose restart # 改 config.json 后必须 restart(env 改动则需 up -d 重建)
docker compose pull && docker compose up -d # 升级:先备份卷(见 CLI 篇)
docker compose down # 停止(卷数据保留)
两个实测注记:
- 重启即掉登录态:session 存内存,优雅关闭才持久化
sessions.csv;docker restart后旧 cookie 全部失效(实测 303 → login),需重新登录。 - 升级前必备份:Go 版已停止发新稳定版(见选型篇 Rust 重写背景),跨大版本(Go→Rust)迁移要走官方迁移指南,届时卷内数据格式会变。
九、常见问题
| 症状 | 原因与处理 |
|---|---|
docker pull 报"这镜像不在白名单" | DaoCloud 加速源白名单拦截;换 docker.1ms.run/reaper99/recipya:v1.2.2 显式拉取后重打 tag(§3.2) |
| 端口映射后 502/拒绝连接 | 应用监听 8078(配置值),不是 EXPOSE 的 8080;映射写 宿主:8078 |
| 导入/导出接口 400 "Connection lost" | 该用户无活动 /ws 连接;自动化场景先建 WS 再 POST(§6.3) |
| 注册后没收到确认邮件 | 未配 SendGrid,邮件只入队;不影响登录,直接登录即可 |
| 匿名访问总跳 /guide/en | 内置向导;设 RECIPYA_SERVER_BYPASS_GUIDE=true |
| 公网部署 cookie 不安全告警 | RECIPYA_SERVER_IS_PROD=true + https URL(IsCookieSecure 依赖该组合) |
十、小结
Recipya 的部署是食谱管理器里最省事的一档:一个 compose service、一个命名卷、一个 8078 端口,首启自动完成迁移与 FDC 营养库下载。真正要花心思的是三件事:镜像 tag 的 v 前缀与 latest≠v1.2.2、URL 导入/导出依赖 WebSocket 的 API 行为、以及"重启掉 session"的会话模型。部署完成后,参数调优见 《Recipya v1.2.2 配置参数详解》,备份恢复与导入导出工具链见 《Recipya v1.2.2 常用 CLI 命令与工具详解》。