1. 一个被忽视的家庭管理需求Grocy到底能解决什么问题冰箱里翻出一袋过期半年的冷冻肉、调料瓶买重了三瓶、出门前根本想不起家里还剩哪些食材——这种场景几乎是每个家庭的日常。为了治这个病我最终选择了一套自托管方案把开源的家庭库存管理工具 Grocy 本地部署在一台常年开机的小主机上再通过反向代理和动态域名实现外部访问出门在外也能随时查看库存、往购物清单里加东西。先说结论Grocy 不是一个记账App它是一套面向家庭和个人的轻量 ERP。它的核心思路是把家里所有物品的进、出、存、耗都管起来让家里还剩什么、缺什么、什么东西快过期了这种问题不再依赖任何人的记忆力。比起商业软件自托管最大的优势是数据完全在自己手里、没有订阅费、功能可以按自己的习惯来调。这篇文章我会从选型、部署、外部访问到日常运维完整过一遍适合有一定动手能力、愿意折腾一台小主机或 NAS 的朋友参考。1.1 家庭场景里的典型痛点家里物品管理的失控通常不是懒而是信息不透明。两个人分头去超市很容易重复购买同一种调料冷藏室深处的东西常常等想起来的时候已经过了保质期周末想做个菜打开冰箱才发现缺了关键配料临时又不想再跑一趟。这些问题的本质是库存信息只存在于某个人的脑子里而且这个数据库还会随着时间自动遗忘。我试过用备忘录、用表格软件记录结果都坚持不了几天——因为每次记录都要手动打开文档、找到对应条目、再修改数量路径太长注定会被放弃。Grocy 这类工具的价值在于它把记录的成本降到极低手机扫码或者点两下就能完成一次入库或出库并且所有数据是结构化的可以自动算出来当前库存够用几天哪些物品即将过期。1.2 Grocy 的核心功能地图Grocy 的功能模块是围绕家庭生命周期设计的我用下来最常用的几个模块如下表模块作用典型使用场景库存管理管物品、数量、单位、保质期、存放位置家里还剩 3 瓶酱油其中 1 瓶还有 20 天过期购物清单手动加购或按低库存阈值自动生成牛奶低于 2 盒时自动出现在购物清单上保质期/任务提醒按到期日和周期任务推送提醒鸡蛋 3 天后到期、每周一打扫厨房餐谱与餐饮计划记录菜谱按用料自动折算采购量计划做 4 人份咖喱自动算出需要买多少土豆统计与历史记录消耗趋势、消费记录看家里每月大概消耗多少纸巾它默认是个 Web 应用局域网内任何设备的浏览器都能访问不挑系统手机、平板、电脑都行。实际体验下来最舒服的用法是手机浏览器 扫码入库时扫一下商品条码出库时再扫一下几乎不用打字。1.3 为什么自建而不是用商业 App市面上有很多购物清单类 App但大多只解决记下来这一层没解决存量还剩多少、什么时候该买这一层。真正常见的问题恰恰是不知道家里有没有、不知道够不够而这是需要完整库存数据才能回答的。商业 App 还有两个让我不太舒服的点一是数据存在别人服务器上家庭消费习惯也是隐私二是想要高级功能基本都要订阅一年下来费用也不少。自托管则是一次性投入设备软件本身开源免费数据全部落在自己的硬盘上。当然自建是有门槛的你需要一台 7×24 小时开机的设备需要会一点命令行出了问题要自己查日志。但如果你本来就在玩 NAS 或者折腾过软路由那 Grocy 的难度大概只是入门级。接下来我把整个部署过程完整写出来。2. 部署前的选型判断机器、系统与安装方式怎么定在动手之前先花十分钟把硬件和安装方式定下来。这一步做对了后面能省一大堆事。2.1 一台常年开机的设备就够了Grocy 对硬件的需求低到令人发指。官方推荐的底线大约是一颗 1GHz 以上的 CPU、512MB 内存、2GB 磁盘空间实际使用中如果只是一个人或两口之家用512MB 内存完全能跑。我自己用的是一台巴掌大的低功耗 X86 小主机整机功耗不到 10 瓦跑 Grocy 加反向代理加备份任务内存占用一直在 40% 左右。你可以根据自己的情况选设备旧笔记本最省钱的选择接上电源当服务器用注意散热和断电自动重启迷你主机/瘦客户机体积小、功耗低适合长期开机NAS如果已经有 NAS直接在 NAS 的容器功能里跑最省心树莓派等单板机ARM 架构完全支持就是写 SD 卡要注意备份低配云主机不想在家里放设备的可以考虑但数据就不在本地了不管用哪种都要给这台设备设置固定局域网 IP建议在路由器里做 DHCP 静态绑定。这一步不做后面配端口映射、反向代理的时候IP 一变就全乱套。2.2 Docker Compose 还是原生安装Grocy 官方提供两种部署方式原生安装PHP SQLite和容器镜像。我的建议很直接能用 Docker Compose 就用 Docker Compose。理由看下面的对比对比项Docker Compose原生安装数据隔离容器隔离数据集中在数据卷依赖系统目录清理麻烦升级一条命令拉新镜像失败可回滚手动替换代码容易留残留资源占用有少量额外开销更省适合 512MB 以下设备排错日志集中在容器查看方便日志分散在系统各处我选 Docker 的核心原因其实是升级和回滚。容器方案升级就是docker compose pull再docker compose up -d如果新版本有问题把旧镜像的标签换回来就能回退而原生安装升级一次要动一堆文件出了问题很难干净地退回上一个版本。唯一的例外是那种内存只有 256MB 或者 512MB 且已经跑满的老设备这时候原生安装会更轻。如果你属于这种情况请直接参考官方文档的原生部署步骤这里不展开。3. 实操用 Docker Compose 把 Grocy 跑起来的完整过程下面这部分按我自己的实操顺序来写每一步都是可以直接抄的。3.1 基础环境准备假设你已经在目标机器上装好了 Debian/Ubuntu 系的 Linux 系统并且能从终端登录。第一步是安装 Docker Engine 和 Compose 插件sudo apt update sudo apt install -y docker.io docker-compose-v2 sudo systemctl enable --now docker不同发行版的包名略有差异比如有的系统里 Docker 官方源安装的插件包名是docker-compose-plugin但装完后的验证方式是一样的docker --version docker compose version两条命令能正常输出版本号说明环境就绪。如果系统里之前装过旧版 docker-composePython 版建议直接卸载避免和docker compose子命令混淆。3.2 编写 compose 文件并启动我习惯把 Grocy 的目录放在/opt/grocy数据单独挂载到./data这样整个服务就是一个目录 一个配置文件备份、迁移、删除都极其干净。mkdir -p /opt/grocy cd /opt/grocy然后创建docker-compose.ymlservices: grocy: image: ghcr.io/grocy/grocy:latest container_name: grocy restart: unless-stopped ports: - 9283:80 volumes: - ./data:/data environment: - TZAsia/Shanghai逐个说明image使用官方镜像仓库的latest标签跟随主版本更新restart: unless-stopped设备重启后容器自动拉起这个对家用服务是必须的ports宿主机 9283 端口映射到容器内 80 端口。9283 是 Grocy 社区约定俗成的端口方便记忆如果你机器上已经有服务占用可以改成 9284 等任意端口volumes宿主机的./data目录挂载为容器内的/data。Grocy 的 SQLite 数据库、用户文件、主题全部存在这里这个目录就是你的全部家当environment.TZ设置时区保证任务提醒和日期显示正确启动docker compose up -d docker compose ps看到状态为Up基本就成了。再确认一下日志docker compose logs --tail50浏览器访问http://主机局域网IP:9283能出现登录页面就是部署成功。提示如果页面打不开先别急着怀疑镜像优先排查宿主机防火墙。很多系统默认开着防火墙需要放行 9283 端口或者直接重启 Docker 后重试。3.3 初始化第一次登录和必做的设置Grocy 的初始账号密码是admin / admin。登录后第一件事就是改密码去用户设置里把管理员密码换成一个强密码这一步没有商量的余地。接下来进入设置Settings页面按自己的习惯调整语言Grocy 有社区维护的中文语言包切到中文后界面会顺眼很多货币与单位设置本地货币以及物品的默认单位克、升、瓶、盒日期格式改成符合本地习惯的年月日格式低库存阈值这是 Grocy 的灵魂设置比如牛奶设成低于 2 盒时提醒之后库存低于阈值会自动进购物清单为了快速理解它的工作流建议先建一个物品试试。以牛奶为例在主数据 → 产品里新建牛奶单位选盒设定保质期默认 7 天。然后在库存页面点购买数量填 3 盒点消耗数量填 1 盒。你会发现库存自动变成 2 盒而且当数量低于你设置的阈值时购物清单里会自动出现牛奶。这个采购入库 → 日常消耗 → 自动补货的闭环就是 Grocy 最核心的用法。多用户方面可以在用户管理里给家人创建账号分配不同的权限角色比如只读、只能操作购物清单、完整权限等避免家人误改主数据。4. 外部访问的三种路线选哪条、为什么本地部署跑通之后下一步就是外部访问。这一步是整个项目里最容易踩坑、也最需要动脑的部分。先讲清楚一个原则外部访问方案不是越高级越好而是越符合你的网络条件越好。4.1 先判断你的网络条件动手之前先回答三个问题你家的宽带有没有公网 IPv4 地址最直接的判断方法登录路由器后台看 WAN 口 IP再去一些显示本机公网 IP 的网页看结果两个一致就是有公网 IP不一致说明运营商做了大内网网络地址转换你的公网地址是运营商共享的。路由器管理权在不在你手里能不能登录后台做端口映射决定了方案 A 和 B 能否实施。有没有自己的域名有域名和不有域名体验差距很大。如果没有可以先申请免费的子域名或几块钱一年的普通域名。回答完这三个问题基本就能锁定路线了你的网络条件推荐路线有公网 IPv4 有域名反向代理 DDNS第 4.3 节有公网 IPv4 没域名DDNS 免费子域名 端口映射第 4.2 节无公网 IPv4大内网隧道方案第 4.4 节4.2 路线一DDNS 加端口映射最基础的解法DDNS动态域名解析解决的是家庭公网 IP 会变这个问题。宽带运营商的公网 IP 通常是动态的今天是这个地址明天可能就是另一个你需要一个固定域名始终指向你当前的 IP。具体做法在路由器后台找到 DDNS 功能填入动态域名服务商提供的账号密码路由器会自动在 IP 变化时更新域名解析记录。完成之后你就算拿到了一个能解析到家里的域名。然后做端口映射在路由器的端口映射/端口转发页面把外网某个端口比如 9283转发到内网 Grocy 主机的 9283 端口。保存之后在手机流量环境下访问http://你的域名:9283能打开就成功了。这条路线最大的问题是很裸你把一个带默认初始密码如果你还没改的 Web 应用直接暴露在公网上。扫描器全天候在扫全网 9283 这类已知端口一旦被扫到暴力破解只是时间问题。所以如果你只能走这条路线至少要做到改掉默认密码、更换外网端口为高位随机端口、有条件就加一层反向代理并且不要只依赖端口映射裸奔。4.3 路线二反向代理 子域名我推荐的路线反向代理的思路是在家庭内网里放一个流量入口比如 Caddy 或 Nginx 反向代理所有从公网进来的 HTTPS 请求先到它再由它按域名分发给内网不同的服务。对 Grocy 来说就是stock.你的域名.com→ 反向代理主机 →http://局域网IP:9283。这样做有几个明显好处公网只开 443 一个端口不用把 9283 直接暴露出去多个内网服务可以共用一个入口将来再加其他服务不用再开端口TLS 加密证书由代理统一管理我最推荐 Caddy因为它能自动申请和续期 HTTPS 证书几乎零配置。假设 Caddy 和 Grocy 在同一台机器上装好 Caddy 后只需要写一个Caddyfilestock.example.com { reverse_proxy 127.0.0.1:9283 }然后在路由器上把外网 443 端口转发到这台主机的 443 端口再把域名的 A 记录解析到你家公网 IPCaddy 检测到请求后会自己完成证书申请并转发到 Grocy。访问https://stock.example.com就能看到一个带小锁标志的 Grocy。如果你更喜欢 Nginx核心配置也给你一段参考证书需要自己用 ACME 客户端申请server { listen 443 ssl; server_name stock.example.com; ssl_certificate /etc/ssl/fullchain.pem; ssl_certificate_key /etc/ssl/privkey.pem; location / { proxy_pass http://127.0.0.1:9283; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; } }坦白说这条路线唯一的前提就是你有公网 IPv4 和域名。满足的话它是在方便和安全之间最平衡的方案。4.4 路线三隧道模式无公网 IP 的解法如果你的宽带没有公网 IPv4运营商大内网端口映射基本是废的。这时候可以考虑隧道模式思路完全反过来不是让公网流量进你家而是你家设备主动向外发起一条加密连接把流量送到云端边缘节点再由边缘节点把公网请求转发回来。最常用的实现是 cloudflared 这个工具。它的快速体验方式非常暴力——在内网机器上执行cloudflared tunnel --url http://127.0.0.1:9283工具会先建立一个稳定的出站连接然后返回一串临时公网地址浏览器打开就能访问 Grocy。整个过程不需要路由器做任何设置也不需要公网 IP、不需要开放端口。长期使用的话可以创建命名隧道并注册一个固定域名指向它再把 cloudflared 注册成系统服务。这样你的 Grocy 就有一个永远不变的公网 HTTPS 地址而且证书是自动管理的。这条路线需要留意两点一是流量会经过第三方边缘节点再回源数据路径变长对隐私比较介意的应用要斟酌二是免费版带宽有限但 Grocy 这种轻量数据页面 少量图片完全够用别用它传视频就行。对于家里没有公网 IP这个硬约束它是性价比最高的方案。5. 外部访问的安全补强默认口令、HTTPS 和备份都不能省无论你选了哪条外部访问路线下面的安全操作都必须做。我把它们叫做上线前的四个必修课。5.1 默认账号是第一道漏洞前面已经提过一次但这里单独拿出来说Grocy 的初始账号admin/admin是公开写在文档里的任何扫到你的服务的人都会先试这个组合。改管理员密码之外还建议做两件事创建自己的日常账号日常浏览用普通权限账号管理员账号只在配置系统时使用给家人的账号分配最小权限比如只能操作购物清单和库存进出不能改主数据Grocy 的权限模型足够细致家庭成员甚至可以做看不到某个物品分类这样的限制。别嫌麻烦提前分好权限能避免很多家人不小心把主数据改乱的悲剧。5.2 传输加密HTTPS 是底线只要服务暴露到公网就必须上 HTTPS。原因很简单HTTP 是明文传输账号密码在网络上裸奔同网络的人抓包就能看到。如果你走的是 Caddy 或隧道方案证书是自动管理的不需要做任何额外操作如果你只做了端口映射那至少要给 Grocy 前面套一层能提供 TLS 的反向代理或者挂一批免费证书自己配置。我个人实测的经验是不要试图给 Grocy 直接配置 TLS把 TLS 交给反向代理层处理业务容器只管 HTTP这样职责清晰以后升级 Grocy 也不会把证书配置搞丢。5.3 访问入口的访问控制暴露公网不等于向全世界开放。两个最实用的控制手段第一个是IP 白名单。如果你的使用场景只是自己出门在外时访问那完全可以在反向代理层限制来源 IP只放行你手机所在运营商的 IP 段或你办公地点的固定 IP。Caddy 可以按 IP 拒绝或放行Nginx 同样支持allow/deny指令。第二个是基础认证叠加。在 Grocy 自己的账号密码外面再套一层基础认证相当于上两道锁。Caddy 里可以在站点块里加basic_auth用工具生成 bcrypt 哈希后配置即可。这样做的好处是即使 Grocy 某个版本出现未授权访问漏洞外面这层基础认证还能挡住一部分攻击。5.4 备份一个文件就是全部家当Grocy 用 SQLite 存数据所有数据就是一个数据库文件备份的简单程度在所有自托管服务里数一数二。我的方案是每天晚上用定时任务把整个data目录打成 tar 包保留最近两周再同步一份到另一个设备。定时备份脚本示例#!/usr/bin/env bash BACKUP_DIR/data/backups/grocy mkdir -p $BACKUP_DIR tar czf $BACKUP_DIR/grocy_$(date %Y%m%d_%H%M).tar.gz -C /opt/grocy data find $BACKUP_DIR -name grocy_*.tar.gz -mtime 14 -delete用 crontab 挂到每天凌晨执行即可。恢复就更简单停掉容器把备份里的data目录原样放回去再启动容器。这里我特别建议你真的做一次恢复演练别等到数据丢了才第一次尝试那样压力会大得多。6. 上线之后的日常运维与踩坑记录部署和外部访问都打通之后剩下的就是长期使用的问题了。这一章把我自己踩过的坑和总结的经验全部倒出来。6.1 升级镜像的正确姿势Grocy 更新频率不低latest标签会经常跟着变。升级本身很简单cd /opt/grocy docker compose pull grocy docker compose up -d但有几个必须遵守的纪律升级前先做一次备份哪怕只是复制一份data目录成本极低收益极高升级后立刻看日志docker compose logs --tail50确认没有报错再离开跨大版本升级比如主版本号变化前先去官网看升级说明确认有没有数据结构变化Grocy 在启动时会对数据库做自动迁移这意味着迁移一旦发生旧版本可能无法直接读取新数据库。所以你如果想回滚不能只把镜像切回旧版本必须连同数据库备份一起回滚。这也是为什么我反复强调升级前备份 data 目录。6.2 我踩过的几个坑第一个坑是数据目录权限问题。有一次我把data目录从备份里恢复回去容器反复重启日志里全是权限报错。原因很简单备份文件是用 root 解包的目录属主变了。解决方法是把data目录的属主改成容器内运行用户的 UID或者在恢复后重新创建容器让目录权限重新初始化。第二个坑是端口冲突。家里设备越来越多某天突然发现 9283 端口被另一个服务占了。这种问题在 compose 文件里改一个映射端口就行但要注意反向代理里的转发目标也要一起改不然会出现代理能通、服务打不开的奇怪现象。第三个坑是路由器的 NAT 回环。在局域网里用公网域名访问自己的服务很多路由器默认是不通或很慢的因为流量需要在 NAT 设备上折返。这往往让人误以为外部访问失败实际上拿手机切到流量再测就通了。判断标准局域网内访问失败不代表外部访问失败一定要用外网环境实测。第四个坑是忘记固定 IP。早期我让设备走 DHCP结果路由器重启后 IP 变了反向代理配置里的192.168.x.x全部失效。后来把所有关键设备都改成静态绑定这类问题再没出现过。6.3 让 Grocy 真正融入日常的习惯很多人的自托管项目死在新鲜劲过了就不用了Grocy 也容易这样。我自己的体会是它能不能坚持用下去取决于你用多小的成本维持数据更新。我的做法是三条约定第一消耗时顺手记——吃完最后一件东西手机拿出来扫码或点一下三秒完成第二只在购物清单上做文章——每次去超市前只看购物清单不看库存让 Grocy 决定该买什么第三别贪多——刚开始只用库存和购物清单两个模块形成习惯后再开保质期提醒和菜谱一上来把所有功能都打开很容易被劝退。Grocy 的手机浏览器页面做得不错可以添加到主屏幕变成类似原生应用的入口配合扫码枪或者手机摄像头扫码日常出入库几乎不用打字。它还有一套完整的 REST API可以生成 API Key 后给家里的自动化脚本或消息机器人调用比如库存不足时往家庭群推送一条提醒——这个我还在折腾等稳定了再单独写一篇。说到底Grocy 只是一个工具真正的价值在于你把它用成一个习惯。从一台小主机、一个 compose 文件开始先把库存 购物清单这个最小闭环跑起来再慢慢往外扩展外部访问、自动化提醒这些玩法。这套系统我用了大半年最大的感受是家里再也没出现过买重三瓶酱油和冰箱深处过期肉这种事了。