这篇内容是关于 NAS 管理打印相关的工具。(Claude Code 4.8 Opus还在继续蹬,让那些中转必封的支持者失望了)
前提准备
“外地 PC/手机、家庭 PVE 虚拟机已通过 WireGuard 组内网,可以正常互相访问;HP 打印机已正常联网。原来给孩子打作业都是通过 PVE 远程控制开机 Windows 虚拟机,把文件发过去再打印。只有适合自己的方案才是最好的!!!
家里 NAS,PVE+Ubuntu(docker 服务)+Windows(平时不开机,打印或者调试家里网络用)+NAS 服务 打印机 HP LaserJet Pro MFP m26nw
那个让我有操作的瞬间
双职工家庭,工作日白天家里只有奶奶帮忙带娃。
每周总有那么 2-4 次,剧本几乎一模一样:低年级的孩子,老师会把每天的作业发到群里,一般口算题、写字之类的是需要打印的。
然后开始我的"远程打印仪式":
电脑连家里 WireGuard 进 PVE 上那台为打印开着的 Windows 虚拟机 Windows 上登微信,下载这张图片(顺便忍受微信桌面版同步那几秒的转圈) 打开文件,Ctrl+P 孩子已经习惯了,看到老师作业内容,如果有需要打印,自己去打印机上面取
一顿操作,整套流程要 2-3 分钟,偶尔打印失败的时候,大概率是没纸了,我给老人或孩子打电话(他们都会加纸)。
这事让我难受的不是哪一步特别难,而是——它实在太有操作了。
打印机自己带 WiFi,已经联网了;NAS(其实是 PVE 上的一台 Ubuntu 虚拟机)也 7×24 开着;我电脑网络畅通。但就因为缺一个"网页版打印入口",我不得不绕道一台 Windows 虚拟机,把一件物理上 3 秒就能完成的事拉长到几分钟。
直到我遇到了 cups-web 这个项目。
cups-web 解决了什么
一句话:它把"打印"这件事变成了一个 HTTP 接口。
往细了说,它做了三件事:
用 CUPS 当打印内核(这是 Linux/macOS 几十年来通用的打印协议栈) 套了一层现代化的 Web UI,浏览器拖文件就能打印 后端自动把 Office、OFD、图片这些格式转成 PDF 再送进 CUPS
对我这个场景来说,最关键的是第二件事——有了 Web UI,远程访问就是水到渠成的。配合 WireGuard 把家里网段打通,我在任何地方打开浏览器都能用。
它给我带来的真正改变
把架构画清楚:
[PC,外地] │ ▼ WireGuard[家里路由器 / 内网 10.8.0.0/24] │ ▼ [PVE Ubuntu VM] ├── cups-web (端口 1180,Web UI) └── cups (端口 631,CUPS 服务) │ ▼ IPP over WiFi[HP LaserJet Pro MFP M26nw] │ ▼ 物理输出 [孩子到家取走作业]
cups-web 完全不暴露公网,所有访问统一走 WireGuard 隧道。安全模型 = 内网服务的安全模型,没有引入任何新的攻击面。
部署实战
“开发者把部署过程和常见问题写的比较清楚,推荐阅读:https://github.com/hanxi/cups-web
先说我的环境:
宿主机:PVE 8.x 虚拟机:Ubuntu 22.04 LTS,4 核 8G VM 网络:桥接(bridge),和打印机在同一个 192.168.x.0/24 网段 Docker:docker-ce + compose v2 打印机:HP LaserJet Pro MFP M26nw,WiFi 接入,路由器后台已绑定 MAC → 固定 IP
第一步:先确认 VM 能"看到"打印机
这一步是后面所有事的前提。在 Ubuntu VM 里:
# 看打印机是否在同一个广播域里avahi-browse -art | grep -i hp# 或者直接 ping 打印机 IPping -c 3 192.168.x.x# 浏览器访问打印机 IP,看它的内置管理页(EWS)能不能打开curl -I <http://192.168.x.x>
如果这三步有任何一步走不通,先别动 cups-web——回头查路由器的 VLAN/AP 隔离设置,或者 PVE 桥接网络的配置。网络通了再继续,否则后面会浪费很多时间在错的地方排查。
第二步:写 docker-compose.yml
官方 docker-compose.yml 是为 USB 直通场景设计的,对网络打印机来说有两处冗余:devices: /dev/bus/usb 不需要。根据自己设备来操作。
我的最终版本,cups 服务采用 bridge 网络模式——这是最干净的方式,不污染 host 网络:
services: cups: image: hanxi/cups:latest container_name: cups environment: - CUPSADMIN=${CUPSADMIN} - CUPSPASSWORD=${CUPSPASSWORD} ports: - "631:631" volumes: - ./.etc:/etc/cups restart: unless-stopped web: image: hanxi/cups-web:latest user: root container_name: cups-web environment: - CUPS_HOST=cups:631 ports: - "1180:8080" volumes: - ./.data:/data - ./.uploads:/uploads depends_on: - cups restart: unless-stopped
几个关键点解释一下:
为什么 cups 用 bridge 而不是 host?网上很多教程会让你用 network_mode: host,理由是"让 CUPS 看到打印机的 mDNS 广播"。但对于网络打印机,我们根本不依赖 mDNS 发现——后面添加打印机时直接填 IP 就行。bridge 模式让容器隔离更清晰,端口冲突风险更小。
容器间通信怎么走?CUPS_HOST=cups:631 这一行——web 容器通过 Docker 内置的 DNS,用服务名 cups 直接访问 cups 容器的 631 端口。完全不需要走 host 网络。
第三步:起容器
在 docker-compose.yml 同级目录建一个 .env:(我首次登录也是用的开发者文档的默认密码,还是跟着作者来吧)
CUPSADMIN=adminCUPSPASSWORD=你自己的强密码
然后启动:
docker compose up -ddocker compose psdocker compose logs -f cups
看到 cups 容器日志里 Listening for connections on 0.0.0.0:631 之类的提示就 OK 了。
第四步:在 631 端口把打印机加进 CUPS(这一步是关键差异)
浏览器打开 http://<VM IP>:631,左上角 Administration → Add Printer,用 .env 里的账号密码登录。
USB 教程到这里会让你在列表里选"USB Printer #1"之类的本地设备。我们不一样:
Other Network Printers 区域,选 Internet Printing Protocol (ipp)
Continue,在 Connection 输入框里填: 把
192.168.x.x换成 M26nw 的实际 IP。ipp://192.168.x.x/ipp/print
Name / Description / Location 随便起,比如
HP_M26nw、家里打印机、客厅重点:勾选 "Share This Printer"——cups-web 通过 IPP 反向连接 CUPS 时,只能看到 shared 的打印机。这个勾不打,后面 cups-web 里就是空列表。
Make 选 HP,Model 选 IPP Everywhere(M26nw 支持 IPP Everywhere,几乎 driverless,比厂商专属驱动省心)
Add Printer
加完之后在 Printers 标签页能看到它,状态是 Idle。打一份测试页(Maintenance → Print Test Page),打印机吐出一张 CUPS 自带的测试图就说明本地链路打通了。

第五步:访问 cups-web 实测
浏览器打开 http://<VM IP>:1180,默认账号密码都是 admin。
登录后第一时间做两件事:
改密码(右上角用户菜单) 在打印机列表里应该能看到刚才那台 HP_M26nw。如果没看到,回 631 检查 Share This Printer 有没有勾上
试着拖一张 PDF 进去,选份数,点 Print。打印机吐纸——本地链路就完成了。
部署实战 Part 2:让它"出门"
到这一步,cups-web 已经能在家里局域网内用了。但我的核心需求是远程,所以需要 WireGuard 把家里网段打通到外面。
WireGuard 的服务端/客户端配置我之前写过一篇详细的:
上面这篇覆盖了配置文件、密钥生成、端口转发、DDNS 这些。这里就不重复了,只讲与打印场景特别相关的两个细节。
一段时间下来的副产品
用了几周之后,发现一些当初没预料到的好处。
PVE 上的 Windows 虚拟机直接基本退役了, 它原来存在的最大意义就是"打印代理"。
等待的焦虑没了,这是最实在的改善。
cups-web 自带打印记录有意外用处。 每条记录有时间、文件名、份数等,比如有时候上午语文老师发了,下午其他老师也需要打印,通过打印记录可以随时发现上午的材料有没有打印过。当然,这事见仁见智,介意隐私的可以把记录保留时间调短。
性能完全不是问题。 一张图片上传到打印开始吐纸大概 2-3 秒,Office 文档 LibreOffice 转 PDF 大概 5-8 秒。4 核 8G 的 VM 跑这个绰绰有余。
几个真踩过的坑
不是所有教程上的坑我都遇到了。下面这几个常见的挑出来说。
坑 1:M26nw 在 cups-web 里能选"双面打印",但它其实是单面机
M26nw 物理上不支持自动双面(要双面得手动翻面再送进去)。但 cups-web 的 UI 上"双面"选项是亮的,可以勾。
第一次手贱勾了,打印机吐出来的是单面,第二张纸又打了同一面内容,浪费了半张纸。
原因是 IPP Everywhere driverless 模式下,打印机能力上报有时候不准。cups-web 显示的是"理论上 CUPS 支持的所有选项",而不是"打印机真实支持的能力"。
解决方式:习惯就好,永远默认单面。如果想彻底干掉这个误操作的可能,可以在 631 端口里给这台打印机改 PPD,把双面选项去掉——但我懒得搞,记住"单面机就是单面机"这一条就行了。
坑 2:路由器重启后打印机 IP 变了 → 一次性解决
这个其实算不上"坑",是部署之前就该做好的事:第一次部署前,先在路由器后台给打印机的 MAC 地址绑定一个固定 IP。
我用的小米路由器,路径是"设置 → 局域网 → DHCP 静态分配"。把 M26nw 的 MAC 填进去,分一个明确的 IP,从此再也没变过。
如果不做这一步,路由器重启或者打印机离线一段时间重连,DHCP 分配的 IP 可能变化,CUPS 里那条 ipp://192.168.x.x/ipp/print 就指向了空气。
cups-web 那台 VM 同理,一起绑了。
这些场景不推荐这套方案
写这种文章的人很多会避开"什么时候不该用",因为劝退看起来像在唱衰自己的方案。但我觉得讲清楚边界才对得起读者。
家里没人能配合"加纸 + 取件" 的,这套也用不起来。再优雅的远程打印,本质还是要有人物理上去做这两件事。
只用苹果设备,且只打 PDF/图片 的,AirPrint 已经够了,没必要折腾这一套。cups-web 的核心价值是"格式转换"和"远程统一入口",这两条对你不适用就划不来。
写在最后
cups-web 是 hanxi 维护的开源项目,MIT 协议。仓库地址:github.com/hanxi/cups-web。
作者在 GitHub Issues 里非常活跃,README 里能看到一连串 issue 编号——大量驱动支持都是根据用户反馈陆续加上去的。作者也维护了微信群(Issue #36),开发节奏稳定,0.x 版本但功能已经相当完整。
? 参考文章
https://github.com/hanxi/cups-web https://www.cups.org/ https://www.ietf.org/rfc/rfc8011.html
本文链接:https://www.kinber.cn/post/6588.html 转载需授权!
推荐本站淘宝优惠价购买喜欢的宝贝:

支付宝微信扫一扫,打赏作者吧~
