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)全闭环与验收清单。

RecipyaRecipya食谱管理DockerDocker ComposeSQLite2026/11/02

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。

二、纠偏须知(部署前必读)

  1. 上游仓库是 reaper47/recipya,不是某些清单里写的 fernandoracane/Recipya(GitHub API 实测 404)。Docker Hub 镜像为 reaper99/recipya。
  2. 版本 tag 带 v 前缀:reaper99/recipya:v1.2.2 存在,1.2.2(不带 v)不存在——实测 docker pull docker.1ms.run/reaper99/recipya:1.2.1 返回 not found。
  3. Go 版仅支持 SQLite:main.go 硬编码 NewSQLiteService(),仓库内无任何 postgres 代码。不存在"PostgreSQL 形态",那是 Rust 重写版(recipya-rs)的能力。
  4. EXPOSE 8080 是误导:镜像 ExposedPorts 写 8080,但应用实际监听端口由配置决定(默认 RECIPYA_SERVER_PORT=8078),端口映射必须对 8078。
  5. 国内镜像加速环境可能遇到 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 psUp,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/login200
注册→登录§6.1303 → 200 + session cookie
手动建食谱§6.2201 → /recipes/1
URL 导入§6.3(带 WS)202 → toast → /recipes/2
数据落卷docker exec recipya-1.2.2-evidence ls /root/.config/RecipyaBackup/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 .../admin200

八、日常运维命令

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                   # 停止(卷数据保留)

两个实测注记:

  1. 重启即掉登录态:session 存内存,优雅关闭才持久化 sessions.csv;docker restart 后旧 cookie 全部失效(实测 303 → login),需重新登录。
  2. 升级前必备份: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 命令与工具详解》。

Recipya Docker Compose 部署指南 - Quzuo