空闲内存不到 50MB,HomeBox 凭什么撑起一整个家庭的物品台账?
先做个小测试:家里那台用了两年的扫地机器人,保修还剩几个月?上次换空调滤网花了多少钱?那把激光测距仪到底放在车库的哪个箱子里?大多数人的答案是“大概”“好像”“我找找”。
物品管理听起来是个小问题,但它的数据结构一点也不小:物品有层层嵌套的存放位置,有发票、说明书、保修卡等附件,有购买价格和维修记录,还需要在保修到期前提醒你。用 Excel 能记,但很快就会变成没人维护的表格;用企业级资产系统(比如 Snipe-IT)又太重,光是配置部门、成本中心就让人放弃。
HomeBox 走的是中间路线:一个专门为家庭场景设计的自托管物品库存系统。它用 Go 编写,默认使用 SQLite,前端打包后直接嵌入二进制,整个容器空闲时内存占用不到 50MB,树莓派、NAS、旧笔记本都能跑。本文不停留在“好用推荐”,而是从数据模型、存储选型、部署安全和生产排坑几个角度,看看这个小工具在工程上做对了哪些取舍。
一、项目定位:为什么家庭库存需要一个专门的系统
HomeBox 最初由开发者 hay-kot 发起,原仓库归档后由社区组织 sysadminsmedia 接手持续维护(仓库地址:github.com/sysadminsmedia/homebox),目前仍在活跃迭代,提供多语言界面(含简体中文)、在线 Demo 与完整文档站。
它的 README 里写了三条设计原则,几乎决定了后面所有技术选型:
| 原则 | 含义 | 对应的技术决策 |
|---|---|---|
| 简单但可扩展 | 开箱即用,不需要复杂配置;需要时也能接入更重的基础设施 | 默认 SQLite + 本地文件存储,可切换 PostgreSQL、S3 类对象存储 |
| 极快、低资源 | 空闲内存小于 50MB | Go 单进程,编译为静态二进制 |
| 便携 | 部署、备份、迁移都足够简单 | 前端嵌入二进制,数据集中在一个 /data 目录 |
把它和几种常见方案放在一起对比,定位会更清晰:
| 方案 | 适用场景 | 部署成本 | 位置层级 | 保修/维护记录 | 多用户 |
|---|---|---|---|---|---|
| Excel / 记事本 | 物品少于几十件 | 无 | 靠手工约定 | 靠手工维护 | 共享文件易冲突 |
| HomeBox | 家庭、工作室、小团队 | 一个容器 | 原生支持无限嵌套 | 原生支持 | 支持(按组隔离) |
| Grocy | 家庭食品与日用品消耗 | 一个容器 | 支持 | 侧重保质期 | 支持 |
| Snipe-IT | 企业 IT 资产 | PHP + MySQL,配置项多 | 支持 | 支持 | 完整的企业权限体系 |
简单说:如果你管理的是“耐用品”(电子产品、工具、家电、收藏品),关心的是“在哪、值多少、保修到哪天”,HomeBox 的数据模型正好贴合。
二、技术架构:一个进程里装下前后端
HomeBox 的整体架构非常克制。后端是一个 Go 服务,路由层使用 go-chi/chi,数据访问层使用 Facebook 开源的代码生成式 ORM ent;前端基于 Nuxt + Vue 3 + Pinia 构建,打包产物在编译时嵌入 Go 二进制,因此最终只需要运行一个进程、暴露一个端口(容器内默认 7745)。

从后端的依赖清单可以看出它在“轻量默认值”和“可选重型能力”之间的分层:
| 能力 | 默认实现 | 可选替换 |
|---|---|---|
| 关系数据库 | SQLite(WAL 模式) | PostgreSQL(通过 pgx 驱动) |
| 附件存储 | 本地目录(file:/// 连接串) |
基于 gocloud.dev 的 S3 / GCS / Azure Blob |
| 内部事件总线 | 进程内内存队列(mem://) |
Kafka、NATS |
| 搜索 | 数据库内查询 | Meilisearch |
| 登录 | 本地账号密码 | OIDC 单点登录 |
| 通知 | 无 | 通过 shoutrrr 推送到 Telegram、Discord、邮件等 |
| 可观测性 | 关闭 | OpenTelemetry 链路、指标与日志导出 |
这种设计的好处是:一个人在树莓派上跑,什么都不用配;一个小团队想把附件放到 MinIO、把数据库放到已有的 PostgreSQL,只需改环境变量,代码路径不变。gocloud.dev 在这里的作用就是把“存到哪”抽象成一个 URL,存储后端的切换不需要改业务代码。

三、数据模型:位置树、资产编号与标签
HomeBox 好用的核心在于它的数据模型,而不是界面。理解了这几个实体,基本就理解了整个系统。
1. 位置(Location)是一棵树
位置可以无限嵌套:家 / 车库 / 红色工具箱、家 / 书房 / 书桌左抽屉。每个位置记录自己的父节点,物品挂在某个位置下。这样“找东西”就变成了一次从叶子节点向上回溯的查询,界面上可以直接展示完整路径。
2. 物品(Item)与资产编号
每件物品除了名称、数量、描述,还有一组和“钱”相关的字段:购买时间、购买价格、购买渠道、保修到期日、是否终身保修,以及出售信息。HomeBox 默认开启资产编号自增(配置项 auto_increment_asset_id 默认为 true),编号以 000-001 的形式展示,便于打印到标签上。
3. 标签(Label)、自定义字段与附件
标签是多对多关系,适合做“电子”“工具”“需要定期保养”这类横向分类;自定义字段用于记录序列号、型号等不规则信息;附件则用于上传照片、发票、说明书和保修卡。维修与保养记录单独成表,每条记录有日期、费用和描述,可以统计某件物品累计花了多少维修费。
4. 用一段可运行代码复刻这个模型
为了直观理解,我们用 Python 标准库 sqlite3 写了一个最小复刻(完整代码见 practice/demo_homebox_inventory.py),覆盖位置树回溯、资产编号格式化、保修扫描和外键约束:
def location_path(db, loc_id) -> str:
rows = db.execute(
"""
WITH RECURSIVE up(id, name, parent_id, depth) AS (
SELECT id, name, parent_id, 0 FROM locations WHERE id = ?
UNION ALL
SELECT l.id, l.name, l.parent_id, up.depth + 1
FROM locations l JOIN up ON l.id = up.parent_id
)
SELECT name FROM up ORDER BY depth DESC
""",
(loc_id,),
).fetchall()
return " / ".join(r[0] for r in rows)
def format_asset_id(n: int) -> str:
s = f"{n:06d}"
return f"{s[:3]}-{s[3:]}"
运行结果:
[sqlite] journal_mode = wal, busy_timeout = 999ms
=== 物品清单(资产编号 + 完整位置路径)===
000-001 激光测距仪 家 / 车库 / 红色工具箱
000-002 NAS 硬盘 8TB 家 / 书房
000-003 移动电源 家 / 书房 / 书桌左抽屉
000-004 冲击钻 家 / 车库 / 红色工具箱
=== 保修扫描(30 天内到期 / 已过期)===
000-003 移动电源 到期 2026-10-09 已过期 3 天
000-001 激光测距仪 到期 2026-11-01 剩余 20 天
=== 按标签统计资产价值 ===
电子 2 件 合计 ¥1,458
数据 1 件 合计 ¥1,299
工具 2 件 合计 ¥788
=== 标签二维码载荷 ===
000-004 -> http://homebox.lan:3100/a/000-004
[约束] 删除仍有子位置的“车库”被拒绝: FOREIGN KEY constraint failed
最后一行值得注意:位置树一旦允许随意删除父节点,就会出现“孤儿物品”。在自己的系统里做树形数据时,外键约束(或等价的应用层校验)是第一道防线;在 HomeBox 里整理位置结构时,也建议先迁移子位置和物品,再删除父位置。
四、为什么默认选 SQLite,以及它的边界在哪
很多人看到“SQLite”会下意识觉得不够“生产级”。但对家庭库存这种场景,SQLite 几乎是最优解:写入量极低(一天新增几条记录),读多写少,数据量通常在几千条以内,单文件意味着备份就是复制一个目录。
HomeBox 的默认 SQLite 连接串里带了几个关键参数:
./.data/homebox.db?_pragma=busy_timeout=999&_pragma=journal_mode=WAL&_fk=1&_time_format=sqlite
| 参数 | 作用 |
|---|---|
journal_mode=WAL |
预写日志模式,读写可以并发,读操作不会被写操作阻塞 |
busy_timeout=999 |
遇到锁时最多等待约 1 秒,而不是立即报 database is locked |
_fk=1 |
开启外键约束,保证位置树、标签关联的引用完整性 |
什么时候应该切到 PostgreSQL?大致有三个信号:一是多人高频同时编辑(比如工作室多人同时盘点入库);二是你已经有一套带备份策略的 PostgreSQL,希望统一纳管;三是数据库文件需要放在网络存储上。切换方式是设置 HBOX_DATABASE_DRIVER=postgres 并补齐主机、用户名、密码等连接参数。
⚠️ 边界提醒:WAL 模式依赖共享内存文件(
-wal与-shm),不要把 SQLite 数据目录放在 NFS、SMB 等网络文件系统上,否则可能出现锁失效甚至数据库损坏。数据目录请放在本地磁盘,用定时备份解决“异地”问题。
五、三分钟部署,以及源文没说的那个必填项
源文给出的 docker run 命令能跑起来,但对照最新官方文档,新版本还需要一个 API Key Pepper(用于对 API 密钥做加盐哈希的服务端密钥)。推荐直接使用 Docker Compose:
# 1. 生成 pepper 并妥善保存(之后不要更换)
openssl rand -base64 48 > hbox.pepper
chmod 400 hbox.pepper
# 2. rootless / hardened 镜像以 UID 65532 运行,需要提前授权数据目录
mkdir -p ./homebox-data
sudo chown 65532:65532 -R ./homebox-data
services:
homebox:
image: ghcr.io/sysadminsmedia/homebox:latest-rootless
container_name: homebox
restart: unless-stopped
ports:
- "3100:7745"
environment:
TZ: Asia/Shanghai
HBOX_AUTH_API_KEY_PEPPER: ${HBOX_PEPPER}
HBOX_OPTIONS_ALLOW_REGISTRATION: "false"
HBOX_WEB_MAX_UPLOAD_SIZE: "20"
volumes:
- ./homebox-data:/data
HBOX_PEPPER="$(cat hbox.pepper)" docker compose up -d
首次部署时有一个顺序问题:注册开关关闭后就无法创建第一个账号。正确做法是先保持注册开启,访问 http://服务器IP:3100 创建管理员账号,再把 HBOX_OPTIONS_ALLOW_REGISTRATION 设为 false 并重启容器。
三个镜像标签的区别:
| 镜像标签 | 运行用户 | 适用场景 |
|---|---|---|
latest |
root | 快速体验 |
latest-rootless |
UID 65532 | 日常自托管推荐 |
latest-hardened |
UID 65532,进一步裁剪 | 暴露到公网或安全要求更高的环境 |
六、扫码入库:把物理世界和数据库连起来
物品管理最怕“录入成本高”。HomeBox 提供了两个降低录入成本的能力:一是标签打印,二是条码查询。
标签打印功能会为物品或位置生成带二维码的标签图片,二维码里编码的是指向该物品详情页的地址。把标签贴在收纳箱外面,手机一扫就能看到箱子里有什么,比翻箱倒柜快得多。标签尺寸、字体、边距都可以通过 labelmaker 相关配置调整,也可以配置打印命令直接对接标签打印机。
条码查询则面向“新买的东西”:扫描商品条形码后,HomeBox 可以调用外部条码数据库补全商品名称等信息(部分数据源需要在配置中填写访问令牌)。

七、生产环境排坑清单
把 HomeBox 当作长期使用的系统,以下几点建议在第一天就做好:
1. Pepper 不要随意更换:它参与 API 密钥的哈希计算,更换后已签发的 API 密钥会全部失效。把它和数据目录一起备份。
2. 备份要带上 WAL 文件:SQLite 在 WAL 模式下,最新数据可能还在 homebox.db-wal 中。最稳妥的做法是短暂停止容器再打包整个数据目录,或使用 sqlite3 homebox.db ".backup backup.db" 做在线一致性备份。
3. 上传大小限制:默认单个附件上限为 10MB,扫描版说明书和高清照片很容易超限,可通过 HBOX_WEB_MAX_UPLOAD_SIZE 调整(单位 MB)。
4. 反向代理后的真实 IP:放在 Nginx、Caddy 之后时,登录限流依赖客户端 IP。需要在确认代理可信的前提下开启 trust_proxy 相关配置,否则所有请求都会被识别为代理 IP。
5. 登录限流默认开启:默认 1 分钟内最多 5 次失败尝试,之后指数退避。家人反复输错密码被暂时锁定属于正常现象。
6. 公网暴露要谨慎:家庭资产清单本质上是一份“家里有什么值钱东西”的清单。优先通过内网、VPN 或零信任隧道访问,确需公网访问时启用 HTTPS 与 OIDC。
八、总结
HomeBox 的价值不在于功能多,而在于它把家庭物品管理这件事的数据结构想清楚了:位置是一棵树,物品挂在树上,钱、保修和维修记录围绕物品展开,标签与二维码把数据库和真实世界连起来。技术上,它用 Go 单进程、嵌入式前端和 SQLite 换来了极低的部署和维护成本,同时通过连接串抽象为 PostgreSQL、对象存储、消息队列和单点登录留好了扩展口。

如果你家里的电子产品、工具和收藏品已经多到需要“找”,花十分钟部署一个 HomeBox,再花一个周末把东西录进去,下次保修到期前收到提醒的那一刻,你会觉得这件事值得。
💡 在线实战体验:本文配套免安装的云端 Linux 交互式实验环境与终端操作,可在 边学边练平台 (https://www.skillup.host/) 直接体验运行验证。