去年年中,我整理工作室设备台账时彻底烦了。手头的笔记本、迷你主机、显示器、路由器、开发板加起来二十多件,Excel表里记了又记,可每次要找某台设备的保修截止日期或者查序列号,都得翻半天。我决定找一个能本地部署、数据完全掌握在自己手里的资产跟踪器。试过几款之后,我留下了 DumbAssets,一直用到现在。
DumbAssets 是一个开源、自托管的资产管理工具,通过 Docker 可以在一台普通 Linux 主机上快速跑起来。它能记录硬件设备、软件许可证、采购信息、存放位置和工单状态,界面简洁,学习成本低。更关键的是,我通过公网 IP 加端口转发、域名解析和前端流量转发这套标准流程,打通了外网访问,人在外面用手机就能随时查设备档案。这篇文章会把部署、外网访问和安全加固的全过程讲透。
1. DumbAssets 具体能管什么,为什么我最终选了它
1.1 不只是记录设备清单
DumbAssets 的核心模型是“资产 + 类别 + 状态”。你可以把一项资产理解为一台路由器、一把机械键盘,或者一个 Windows 许可证。每项资产可以录入品牌、型号、序列号、采购日期、保修截止、供应商、存放位置、当前状态(在库 / 已分配 / 维修中 / 已报废)。它就像一个专门为设备管理设计的迷你 CRM,只不过“客户”换成了你手头的每一样东西。
我自己的习惯是:把工作室的网络设备、服务器、显示器、键鼠外设、甚至充电器都录进去。日常管理时最常用的是“搜索 + 筛选”,比如“维保到期前 30 天的设备”“所有分配给某个人的笔记本”,DumbAssets 的筛选功能基本能满足。它还会为每项资产生成一个二维码,打印出来后贴在设备上,要查信息时用手机扫一下就能跳转到对应详情页,这个功能在盘点时特别好用。
1.2 和 Snipe-IT 之类的方案比,差在哪
很多人一提到开源资产跟踪器,第一个想到的是 Snipe-IT。我最初也装了 Snipe-IT 试玩,功能确实多,但部署完成后整个后台菜单一层套一层,它更像是面向企业 IT 部门做审批流和大型资产台账的工具。对个人和三五人的小团队来说,这种复杂程度有点过重。DumbAssets 的定位正好是反过来的,它只保留核心字段和常用操作,界面贴近现代 Web 应用,录资产和查资产都很快。
| 对比项 | DumbAssets | Snipe-IT |
|---|---|---|
| 部署负担 | Docker 启动简单,依赖少 | 依赖较多,配置项多 |
| 学习成本 | 低,几分钟能上手 | 中高,需要熟悉概念 |
| 字段灵活度 | 基础字段够用 | 自定义字段更多 |
| 界面风格 | 现代简洁 | 传统后台风格 |
| 适合场景 | 个人 / 工作室 / 小团队 | 企业规范化管理 |
当然,如果你需要复杂的审批流、多部门隔离或者强自定义表单,DumbAssets 可能不够。选型时要先想清楚:你的资产也就几十件到两三百件,真要上一套重型的工单系统吗?我的答案是没必要,轻量才是长期维护下去的前提。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 部署前的基础准备:不把这一步做好,后面全是坑
2.1 主机要求
DumbAssets 可以部署在 x86 或 ARM 架构的 Linux 主机上。我用的是家里一台闲置的 x86 迷你主机,配置不算高:双核 CPU、4GB 内存、120GB SSD。运行时 DumbAssets 应用容器和 MySQL 容器加起来大概占 1GB 内存,日常空载时 CPU 几乎不动。如果你手头只有树莓派,也能跑,只是首次拉取镜像时时间会久一些,后面使用没有明显区别。
操作系统我建议用 Ubuntu 22.04 LTS 或 Debian 12,这两个系统的 Docker 支持最成熟,遇到问题的排查资料也最多。Windows 也可以装 Docker Desktop 跑,但不建议长期做自托管——文件卷挂载和开机自启的体验都要差一截,而且重启后容器编排经常不是你想要的顺序。
2.2 装好 Docker 和 Compose
部署前先装 Docker 和 Compose 插件。Ubuntu 上最省事的方式是走 apt 仓库安装。装完之后建议把当前用户加入 docker 组,避免每次敲 sudo:
bash复制sudo apt update
sudo apt install docker.io docker-compose-v2
sudo systemctl enable --now docker
sudo usermod -aG docker $USER
改完用户组后重新登录一次,然后检查版本:
bash复制docker --version
docker compose version
这一步通常不会出问题,偶尔会遇到旧版本 docker-compose(Python 版)和 docker compose 插件混用的情况。我的建议是统一使用 docker compose(空格形式)而不是 docker-compose,新版 Compose 的配置语法更严格,也支持 depends_on.condition 这类健康检查条件,方便控制容器启动顺序。
2.3 目录与端口规划
部署前先把宿主机目录规划好。我习惯在 /opt/dumbassets 下放置所有文件:
text复制/opt/dumbassets
├── docker-compose.yml
├── .env
└── mysql_data/
端口规划上,DumbAssets 容器的默认 Web 端口是 80,我在 compose 里把它映射到宿主机的 8080,因为 80 端口通常要留给 Caddy 或其他 Web 服务做前端监听。如果你准备让 Caddy 直接占用宿主机的 80/443 端口做入口,那么 DumbAssets 就按 8080 映射到内网,Caddy 再把公网请求转发到 8080 即可。这个设计后面讲外网访问时会很清晰。
映射好文件系统后记得提前给 MySQL 数据目录授权,避免容器启动时因为权限问题无法写入。Docker 会以 root 身份创建挂载目录,但 MySQL 容器内的 mysql 用户对宿主机目录有权限要求。如果启动后看到 Permission denied,直接执行 sudo chown -R 999:999 /opt/dumbassets/mysql_data 就能解决。
3. 用 Docker Compose 把 DumbAssets 跑起来
3.1 compose 文件逐段拆解
直接贴出我的 docker-compose.yml 供参考:
yaml复制services:
dumbassets:
image: ghcr.io/dumbware/dumbassets:latest
container_name: dumbassets
restart: unless-stopped
ports:
- "8080:80"
env_file:
- .env
depends_on:
mysql:
condition: service_healthy
mysql:
image: mysql:8.0
container_name: dumbassets-mysql
restart: unless-stopped
env_file:
- .env
volumes:
- ./mysql_data:/var/lib/mysql
healthcheck:
test: ["CMD", "mysqladmin", "ping", "-h", "localhost"]
interval: 5s
timeout: 3s
retries: 10
对应的 .env 文件:
bash复制APP_ENV=production
APP_DEBUG=false
APP_URL=https://assets.example.com
APP_KEY=
DB_HOST=mysql
DB_PORT=3306
DB_DATABASE=dumbassets
DB_USERNAME=dumbassets
DB_PASSWORD=请换成一串强密码
MYSQL_DATABASE=dumbassets
MYSQL_USER=dumbassets
MYSQL_PASSWORD=请换成一串强密码
MYSQL_ROOT_PASSWORD=请换成另一串强密码
逐项说明几个容易踩坑的点。
APP_KEY 是 Laravel 框架的加密密钥,空着启动会直接报错。生成方式是用本机 openssl:
bash复制openssl rand -base64 32
把输出结果填到 APP_KEY= 后面。这里有个细节:Laravel 要求 key 带 base64: 前缀。openssl 输出的是纯随机字符串,保险起见直接手动在开头加上 base64: 前缀,比如:
bash复制APP_KEY=base64:WXV3uXbp9jj3tSk8...
APP_DEBUG=false 非常重要。很多本地部署教程喜欢开 debug 方便看报错,但一旦暴露到公网,debug 开启等于把服务器路径、数据库连接信息等敏感堆栈直接展示给访客,必须设为 false。
数据库部分,DumbAssets 通过 DB_* 变量连接 MySQL,MySQL 容器则通过 MYSQL_* 变量初始化库和账号。两组变量里库名和用户名要保持一致,这是最容易导致连接失败的错误来源:两边密码写岔了,应用起不来,健康检查却显示数据库容器正常。
写密码时还要注意,别用 $、#、& 这类会被 shell 或 Compose 特殊处理的字符。建议用大小写字母加数字加下划线的组合,比如 Da_2025_asset7x。用 .env 文件管理密钥的另一个好处是,密钥不直接散落到 compose 文件里,也方便后续把配置纳入只读备份。
3.2 启动、初始化和录入第一个资产
配置文件就绪后,在 /opt/dumbassets 下执行:
bash复制docker compose up -d
首次启动会拉取镜像,耗时取决于网络。拉取完可以用 docker compose ps 查看状态,两个容器都显示 running / healthy 后再访问 http://服务器IP:8080。
DumbAssets 首次访问会进入初始化页面,需要设置网站名称、管理员邮箱和密码。这里建议用你常用的可靠邮箱,但密码必须另设强密码,不要和邮箱密码共用。初始化完成后登录后台,左侧导航把功能分得比较清楚:仪表盘、资产、类别、位置、供应商、用户、工单。
我录入第一个资产的实操顺序是:
- 先在“位置”里添加“工作室 A 区”“储物间”这类地点;
- 再在“供应商”里添加渠道商或经销商;
- 然后在“类别”里建好“笔记本”“显示器”“开发板”“网络设备”;
- 最后回到“资产”新建,填序列号、采购日期、保修截止、分配人。
录完第一台后你会发现,后续的资产录入基本就是选下拉框 + 填数字,熟练之后一台设备一两分钟就能录完。建议录资产时把序列号、保修截止和采购价格这三项当成必填项,后面统计资产总值、安排续保都靠这些字段。
3.3 数据持久化和备份意识
Docker 容器是“随时可以销毁重建”的。不要因为 MySQL 容器平时很稳定就忘记备份。compose 里已经把 /var/lib/mysql 挂到了宿主机 ./mysql_data,这是第一层保障——容器删了数据还在。但如果宿主机磁盘损坏,依然会全丢。所以我每周做一次 SQL 逻辑备份,命令如下:
bash复制docker exec dumbassets-mysql sh -c 'exec mysqldump --all-databases -uroot -p"$MYSQL_ROOT_PASSWORD"' > backup_$(date +%F).sql
备份文件再同步到另一台机器或网盘,才算真正安全。也可以用 cron 每天凌晨跑一次:
bash复制0 3 * * * cd /opt/dumbassets && docker exec dumbassets-mysql sh -c 'exec mysqldump --all-databases -uroot -p"$MYSQL_ROOT_PASSWORD"' > backup_$(date +\%F).sql
注意 cron 里 % 需要转义,否则会被当换行处理。这个细节我当初踩过一次,输出文件一直间隔很久才有一次,排查后才发现是 cron 的 % 解析问题。
4. 打通公网访问:从局域网到随时随地
4.1 先搞懂公网流量是怎么进到内网的
本地部署的默认状态是只能内网访问。原因在于,普通家庭或办公宽带下,服务器并没有真正的公网 IP,它身处内网,路由器通过 NAT 让设备共享一个公网出口,但这个出口是“有出没进”的——外部主动发起的连接并不会自动转发到某台内网主机。
要打通外网访问,本质上是做两件事:一是让路由器知道“公网某个端口的入站请求,转发给内网哪台机器的哪个端口”,这叫端口转发;二是让访问者能通过一个固定的地址找到你家宽带的公网 IP,这靠域名和动态域名解析。搞清楚这两点,后面的配置就都是顺理成章。
4.2 路由器端口转发与动态域名解析
先说端口转发。登录路由器后台,找到“端口映射”或“虚拟服务器”菜单,添加一条规则:
| 外部端口 | 内网 IP | 内网端口 | 协议 |
|---|---|---|---|
| 8443 | 192.168.1.100 | 8443 | TCP |
这里我用 8443 而不是 443,原因很现实:家宽环境下 80 和 443 这两个默认 Web 端口经常无法正常对外提供访问,即便映射了也连不通。用 8443 这类非标准端口是最稳的做法。如果你在云服务器上部署,80 和 443 基本都能用,就不需要绕。
如果你手头的宽带是动态公网 IP(大多数家宽都是隔段时间变一次 IP),每次变之后,之前解析到旧 IP 的域名就失效了。解决办法是启用 DDNS 动态域名解析。一般路由器自带 DDNS 客户端,绑定花生壳或各类云厂商的 DDNS 插件都可以。主机侧也可以用一个小容器定期调用云解析 API 更新记录。我目前的做法是路由器自带 DDNS,配置完成后域名始终指向当前公网 IP。
到这里,访问链路已经通了一半:外部请求 → 域名 → 公网 IP:8443 → 路由器 DNAT → 服务器:8443。但 DumbAssets 容器还只监听了 8080,我们需要一个前端流量转发入口,把服务器的 8443 请求转给 8080 容器。
4.3 用 Caddy 做前端入口并自动搞定 HTTPS
这里我强烈推荐 Caddy,而不是传统 Nginx 加 Certbot 的组合。Caddy 最核心的价值是自动申请和续期 HTTPS 证书。只要你在配置里写清楚域名,它会把证书申请、验证、续期的流程全部消化掉,不用自己写 certbot 定时任务,也不用手动改 Nginx 配置。
安装 Caddy:
bash复制sudo apt install -y debian-keyring debian-archive-keyring apt-transport-https curl
curl -1sLf 'https://dl.cloudsmith.io/public/caddy/stable/gpg.key' | sudo gpg --dearmor -o /usr/share/keyrings/caddy-stable-archive-keyring.gpg
curl -1sLf 'https://dl.cloudsmith.io/public/caddy/stable/debian.deb.txt' | sudo tee /etc/apt/sources.list.d/caddy-stable.list
sudo apt update
sudo apt install caddy
Caddy 的配置集中在 /etc/caddy/Caddyfile。我的配置如下:
caddyfile复制assets.example.com:8443 {
reverse_proxy 127.0.0.1:8080
}
这里把域名替换成你真正拥有的域名。配置保存后执行 sudo systemctl reload caddy,Caddy 会立刻尝试申请证书。申请成功后,用浏览器访问 https://assets.example.com:8443 就能看到 DumbAssets 的登录页,地址栏带锁标志。
这里有个关键前提:你必须对该域名有 DNS 控制权,Caddy 默认的证书申请方式需要验证域名所有权。如果 80 和 443 端口都被限制,证书申请可能失败。这种情况我建议不要在家宽环境硬撑,直接在云服务器上部署前端入口,或者改用 DNS 验证方式申请证书,Caddy 有对应的 dns 插件。配置稍微复杂一些,但这条路是通的。
如果你不想用 Caddy,用 Nginx 做前端入口也是常规方案,大致配置如下:
nginx复制server {
listen 8443 ssl;
server_name assets.example.com;
ssl_certificate /etc/ssl/assets.example.com/fullchain.pem;
ssl_certificate_key /etc/ssl/assets.example.com/privkey.pem;
location / {
proxy_pass http://127.0.0.1:8080;
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;
}
}
不过证书获取和续期都要自己搞,对比下来 Caddy 省心太多。实测里 Caddy 占用的内存不到 30MB,对低配小主机很友好。
4.4 外网访问不通时的排查链路
一次完整的配置很可能不会一次通过。我列一份排查顺序:
- 先在内网测试
https://服务器内网IP:8443,如果不通,问题在服务器自身,检查 Caddy 是否监听 8443、防火墙是否放行; - 内网通过再测
https://公网IP:8443,不通就检查路由器端口映射是否填错内网 IP; - 公网 IP + 端口通了,再测域名,不通则检查 DDNS 是否解析到当前公网 IP;
- 域名也通了但打不开,最后看容器日志:
docker compose logs -f dumbassets。
这个顺序可以避免在错误层面浪费时间。特别是第 2 步,若直接把 192.168.1.100 填错成 192.168.1.101,外部请求会到路由器后无路可走。还有一点别忘了,有些路由器有防火墙 ACL 设置,如果之前配过“禁止内网访问外网某端口”之类的规则,端口转发也可能受影响,配置后最好先看一眼 ACL 是否放行了对应端口的入站方向。
5. 暴露到公网之后的安全加固清单
从内网搬到公网,相当于你的 DumbAssets
