使用 fnOS 将老旧 USB 打印机改造成网络打印机
以联想 M7206 为例,零成本让没有网口的老打印机支持 Windows / macOS / 安卓 / iPhone 全家桶无线打印。
本文不仅给出标准流程,更重要的是复盘了实践中真实踩过的 9 个坑 和一套 分层排障方法论——即使你的打印机型号不同,这套思路也通用。
一、为什么这么做
很多人家里都有一台"还能打但没网口"的老激光打印机,比如联想 M7206。想让它变成网络打印机,常见选择:
| 方案 | 成本 | 评价 |
|---|---|---|
| 买打印服务器盒子 | 100~200 元 | 能用,但功能单一 |
| 挂在常开的电脑上共享 | 0 元 | 电脑必须开机,体验差 |
| ESP32 等单片机 DIY | 40 元 | 极客玩具,无队列、稳定性一般 |
| NAS(fnOS)+ Docker CUPS | 0 元 | NAS 本来就 24 小时开机,体验最接近真正的网络打印机 |
思路很简单:打印机 USB 线插到 fnOS 主机上,在 Docker 里跑一个 CUPS 打印服务,通过 IPP 协议共享给全家设备。CUPS 是 Linux/macOS 的标准打印系统,自带 Web 管理界面、打印队列、IPP 共享,配合 Avahi 广播还能实现 AirPrint/安卓自动发现。
最终效果:
Windows / Mac / 手机 ──IPP──> fnOS(Docker CUPS) ──USB──> 联想 M7206
二、准备工作
2.1 硬件连接
打印机 USB 线插到 fnOS 主机本体的 USB 口(不是路由器、不是电脑),打印机开机。
2.2 确认系统识别
SSH 登录 fnOS,执行:
lsusb
看到类似这样的行就说明硬件识别 OK:
Bus 003 Device 003: ID 17ef:5629 Lenovo M7206
如果看不到,先换 USB 口、换线、确认打印机开机,这一步不通后面全白搭。
三、部署 CUPS 容器
3.1 为什么用命令行而不是 fnOS 图形界面
fnOS 的 Docker 图形界面可以拉镜像建容器,但已创建的容器不能补加"特权模式"和"设备映射",而这两个恰恰是 USB 打印机能不能被容器看到的关键。所以直接命令行一步到位,避免返工。(坑 #1,详见第五章)
3.2 一条命令部署
fnOS 的 Docker 镜像仓库里搜 CUPS,ydkn/cups 是下载量最大的一个,就用它:

# 建配置持久化目录(注意:只挂 /etc/cups,千万别挂 /var/spool/cups,原因见坑 #8)
sudo mkdir -p /vol1/1000/docker/cups/etc
# 如果你的存储路径不是 /vol1/1000,先 ls /vol1 确认
sudo docker run -d \
--name cups \
--restart unless-stopped \
--privileged \
-p 631:631 \
-v /dev/bus/usb:/dev/bus/usb \
-v /vol1/1000/docker/cups/etc:/etc/cups \
-e CUPSADMIN=admin \
-e CUPSPASSWORD=改成你的密码 \
ydkn/cups
参数说明:
--privileged:特权模式,容器访问 USB 设备必需;-v /dev/bus/usb:/dev/bus/usb:把整个 USB 总线映射进容器;-p 631:631:CUPS 的 Web 管理和 IPP 打印端口;-v ...:/etc/cups:配置持久化(打印机定义、共享设置),重建容器不丢;CUPSADMIN / CUPSPASSWORD:Web 管理界面的账号密码。
3.3 验证 USB 进了容器
sudo docker exec cups ls /dev/bus/usb/003/
能列出数字(如 003)就 OK。
四、配置 CUPS
4.1 打开 Web 管理页
浏览器访问 http://你的NAS-IP:631。
坑 #2:点 Administration / Add Printer 时可能跳出一个"需要升级"页面,让你访问
https://172.17.0.2:631/admin/——那是容器内部 IP,永远打不开。这是 CUPS 强制管理操作走 HTTPS 的机制。解决办法:手动把地址改成https://你的NAS-IP:631/admin/(浏览器会提示自签名证书不受信任,点"继续前往"即可)。
4.2 开启共享(必须)
Administration → 右侧 Server Settings,勾选:
- ✅ Share printers connected to this system
- ✅ Allow printing from the Internet(名字吓人,实际意思是"允许局域网内其他机器提交任务",必须勾)
- ✅ Allow remote administration
点 Change Settings,服务会自动重启几秒。

4.3 添加打印机
-
Administration → Add Printer(登录账号密码就是环境变量里设的);
-
Local Printers 里应该能看到
Lenovo M7206,选中 → Continue;- 如果是空的 → 容器没认到 USB,回到 3.3 检查(坑 #3);

-
Name 改成
M7206之类纯英文无空格的名字(会成为共享 URL 的一部分),✅ 勾 Share This Printer; -
驱动选择见下一节。
4.4 驱动选择(贴牌机型的关键知识)
联想 M 系列激光机(M7206/M7216/M7400 等)大多是兄弟(Brother)方案贴牌,CUPS 里没有 Lenovo 驱动,要用兄弟的:
- 首选 brlaser:社区给兄弟 GDI 激光机写的开源驱动,M7206 与 DCP-1510/HL-1110 同代引擎,实测
DCP-1510完美兼容; - 备选:Brother HL-2140 的 Foomatic 驱动(hl1250 / hpijs-pcl5e),但依赖 foomatic 组件,链条更长更容易出问题(我们就栽在这里,见坑 #6)。

brlaser 不在基础镜像里,先补装组件(这里连着坑 #4、#5、#6,建议直接照抄第五章对应段落一次装对):
# ydkn/cups 基于 Debian 10(buster),官方源已下线,先切换到存档源
sudo docker exec -it cups bash -c "printf 'deb http://archive.debian.org/debian buster main\ndeb http://archive.debian.org/debian-security buster/updates main\n' > /etc/apt/sources.list && apt-get -o Acquire::Check-Valid-Until=false update"
# 一次装齐:过滤器链 + 字体 + brlaser 驱动 + PDF 工具
sudo docker exec -it cups bash -c "apt-get -o Acquire::Check-Valid-Until=false install -y cups-filters ghostscript poppler-utils fontconfig fonts-dejavu-core poppler-data qpdf printer-driver-brlaser printer-driver-hpijs"
装完在容器里直接指定驱动(比 Web 界面点来点去快):
sudo docker exec cups lpadmin -p M7206 -m "drv:///brlaser.drv/br1510.ppd"
sudo docker exec cups cupsenable M7206
可以用
sudo docker exec cups lpinfo -m | grep -i brlaser查看所有 brlaser 支持的型号,如果你的联想机型打出来是白纸,就换 br7055、brl2300d 等挨个试。
4.5 打测试页验证
Web 界面:Printers → 点打印机 → Maintenance 下拉 → Print Test Page。
或命令行(更可控):
sudo docker exec cups bash -c "printf 'CUPS PRINT TEST OK\n' > /tmp/t.txt"
sudo docker exec cups lp -d M7206 /tmp/t.txt
正常出纸,服务器侧就大功告成了。 如果失败,别慌,直接进第五章对号入座。
五、踩坑实录与排障方法论(本文精华)
我们这次部署总共踩了 9 个坑,按出现顺序复盘。每个坑都给出现象 → 原因 → 解法。
坑1:fnOS 图形界面建不了"完整"的容器
- 现象:容器建好后 CUPS 的 Add Printer 页 Local Printers 是空的。
- 原因:fnOS 的 Docker 界面不允许给已建容器补加特权模式和
/dev/bus/usb设备映射,而界面上的"命令"输入框是容器内启动命令,干不了这个事。

- 解法:删掉容器,用 3.2 的
docker run命令行重建。凡是需要特权/设备映射的容器,直接命令行建。
坑2:管理页跳转"需要升级 / 172.17.0.2"
- 现象:点 Add Printer 跳到
https://172.17.0.2:631/admin/,打不开。 - 原因:CUPS 强制管理操作走 HTTPS,自动跳转用了容器内部 IP。
- 解法:手动访问
https://NAS真实IP:631/admin/,接受自签名证书。
坑3:Local Printers 空白
-
现象:
lsusb在宿主机能看到打印机,容器里 Add Printer 却看不到。
-
原因:USB 总线没映射进容器,或特权模式没开。
-
解法:重建容器时确认
--privileged和-v /dev/bus/usb:/dev/bus/usb都在;验证docker exec cups ls /dev/bus/usb/003/。另外 USB 打印机是插拔后才被容器感知的,后插打印机就重启一次容器。
坑4:apt 全部 404
- 现象:容器里
apt update一堆404 Not Found。 - 原因:ydkn/cups 基于 Debian 10(buster),官方源已下架,迁移到了存档站。
- 解法:换源到
archive.debian.org,并加Acquire::Check-Valid-Until=false跳过过期时间戳校验(命令见 4.4)。
坑5:管理页突然 404 / "Filter ... not found"
-
现象:装完几个包之后,Administration 页面直接"未找到";日志里
admin.cgi not available、Filter "texttopdf" not found。
-
原因:镜像本身精简,缺组件;安装/卸载过程的依赖解析还可能把 cups 本体搞坏。
-
解法:
apt-get install --reinstall cups cups-common补回 Web 组件,再装cups-filters。教训:在容器里做 apt 操作后,先看一眼 Web 界面和日志再往下走。
坑6:所有驱动都报 "Filter failed"
-
现象:换什么驱动,测试页都是
stopped "Filter failed"。
-
排查过程(这是全套方法论的第一次实战):
- 开调试日志:
docker exec cups cupsctl --debug-logging,重打后tail -80 /var/log/cups/error_log; - 日志显示
bannertopdf/pdftopdf报 "can't find PDF header",foomatic-rip报 "Unknown filetype",Sent 0 bytes; - 逐级定位:texttopdf 失败 → 发现容器一款字体都没装(渲染 PDF 需要字体)→ 装
fonts-dejavu-core; - 字体好了 foomatic-rip 还是不行 → 发现
foomatic-filters包里的古董版 foomatic-rip(不认识 PDF 工作流)覆盖了 cups-filters 的现代版 →apt remove foomatic-filters foomatic-db-engine并apt install --reinstall cups-filters。
- 开调试日志:
-
教训:老教程让你装的
foomatic-filters在 PDF 工作流时代是负资产,现代的 foomatic-rip 在cups-filters包里。最终我们干脆放弃 foomatic 路线,换了完全绕开它的 brlaser 驱动。
坑7:任务卡住不动(pending)
-
现象:测试页提交后一直 pending,打印机动都不动。
-
原因:之前某次失败后,CUPS 按默认策略(ErrorPolicy=stop-printer)自动把打印机暂停了,Web 界面状态是
Paused。
-
解法:
docker exec cups cupsenable M7206恢复;并根治:
sudo docker exec cups lpadmin -p M7206 -o printer-error-policy=retry-job
坑8(终极坑):过滤器单测全过、cupsd 调度必挂
- 现象:手动跑
texttopdf、pdftopdf、gstoraster、rastertobrlaser每一个都正常,甚至-o raw把数据直接灌给打印机都能出纸——但只要通过 cupsd 正常打印,过滤器链就拿到"损坏的 PDF",全链崩溃。 - 排查:对比两种场景的环境变量,发现 cupsd 强制
TMPDIR=/var/spool/cups/tmp——而我们把/var/spool/cups挂载到了宿主机目录。fnOS 的存储文件系统对这些过滤器创建临时文件的操作不兼容,导致临时文件损坏。 - 解法:重建容器,去掉
/var/spool/cups挂载(打印队列本来就是易失数据,不需要持久化)。改完立刻全链路打通。 - 教训:NAS 上玩 Docker,挂载目录要克制——只挂真正需要持久化的(这里是
/etc/cups),随手多挂的目录可能引入诡异的文件系统兼容性问题。
坑9:手机搜不到 / 显示"连接异常"
-
现象 A(搜不到):容器是 bridge 网络 + avahi 没运行,mDNS 广播出不来。
- 解法:装
avahi-daemon dbus libnss-mdns,容器改--network host重建,启动命令改为bash -c "service dbus start && service avahi-daemon start && cupsd -f"。注意 host 模式和-p端口映射不能同时用。另外检查路由器的 AP 隔离/访客网络会阻断组播。
- 解法:装
-
现象 B(能搜到但连接异常):mDNS 把 NAS 的 IPv6 地址给了手机,而 cupsd 只监听 IPv4。

- 解法:手机端手动按 IPv4 地址添加(填 NAS 的
192.168.x.x),一劳永逸。验证监听情况:docker exec cups bash -c 'grep -i 0277 /proc/net/tcp /proc/net/tcp6'(0277 即 631 端口的十六进制)。
- 解法:手机端手动按 IPv4 地址添加(填 NAS 的
附:通用排障方法论(比答案更重要)
这次排查最大的收获是一套分层定位法,任何打印问题都能套:
第 1 层:硬件识别 lsusb(宿主机)→ ls /dev/bus/usb(容器)
第 2 层:服务可用 Web 界面能开?lpstat -t 队列状态?
第 3 层:过滤器链 开 debug 日志,看哪个 PID 退出码非 0
第 4 层:单件验证 手动跑过滤器(注意全路径 /usr/lib/cups/filter/xxx)
第 5 层:端到端 手动串联整个链,最后 -o raw 直灌验证硬件
两个特别好用的技巧:
- 手动逐级跑过滤器:
docker exec cups bash -c '/usr/lib/cups/filter/texttopdf 1 root t 1 "" /tmp/t.txt > /tmp/t.pdf',每一步的产物用qpdf --check、ls -la验证,哪一步产物坏了哪一步就是凶手; - raw 直灌验证硬件:链条产物(如 brlaser 输出的
.br文件)用lp -d M7206 -o raw 文件绕过所有过滤器直接发给打印机——出纸就证明"驱动+硬件"没问题,问题只在调度层。这一招直接把排查范围砍掉一半。
还有三个日志的正确打开方式:
sudo docker exec cups lpstat -t # 队列状态(paused 是常客)
sudo docker exec cups tail -50 /var/log/cups/error_log # 过滤器报错
sudo docker exec cups tail -20 /var/log/cups/access_log # 客户端到底来没来、返回什么码
六、各终端连接方式
服务器调通后,全家设备连接都是一分钟的事。
Windows
-
先装联想官网的 M7206 Windows 驱动;
-
设置 → 打印机和扫描仪 → 添加设备 → 手动添加 → 按名称选择共享打印机,填:
http://NAS-IP:631/printers/M7206 -
选择 M7206 驱动 → 完成。Windows 用自己的驱动渲染,效果和直插电脑完全一致。
macOS
添加打印机,协议选 IPP,地址 ipp://NAS-IP:631/printers/M7206。开了 Avahi 的话会直接在列表里自动出现。
安卓
- 自动发现:开了 Avahi + host 网络后,设置 → 打印 → 默认打印服务,直接能看到
M7206 @ fnos-主机名; - 连不上就手动:添加打印机 → 按 IP 添加 → 填 NAS 的 IPv4 地址(绕开 IPv6 解析问题,见坑 #9);
- 系统打印服务不好使就装 Mopria Print Service(打印行业标准,兼容性最好)。
iPhone / iPad
开了 Avahi 后 AirPrint 直接搜到,照片/文件里分享 → 打印即可。
七、固化与日常维护
7.1 固化镜像(重要!)
容器里 apt 装的所有组件,容器一删就没。调通后立刻提交成自己的镜像:
sudo docker commit cups cups-m7206:fixed
以后重建容器用这个镜像,命令模板(注意依然没有 spool 挂载):
sudo docker run -d \
--name cups \
--restart unless-stopped \
--privileged \
--network host \
-v /dev/bus/usb:/dev/bus/usb \
-v /vol1/1000/docker/cups/etc:/etc/cups \
-e CUPSADMIN=admin \
-e CUPSPASSWORD=你的密码 \
cups-m7206:fixed \
bash -c "service dbus start && service avahi-daemon start && cupsd -f"
7.2 日常"罢工"三板斧
打印机突然打不了,按顺序查:
sudo docker exec cups lpstat -t # 队列是不是 paused/disabled → cupsenable 恢复
sudo docker restart cups # USB 拔插过/容器异常 → 重启容器
sudo docker exec cups tail -20 /var/log/cups/error_log # 看具体报错
7.3 一张备忘表
| 项目 | 值 |
|---|---|
| 管理页面 | http://NAS-IP:631 |
| 共享地址 | http://NAS-IP:631/printers/M7206 |
| 驱动 | brlaser(Brother DCP-1510) |
| 固化镜像 | cups-m7206:fixed |
| 配置目录 | /vol1/1000/docker/cups/etc(别删) |
| 铁律 | 重建容器不要挂 /var/spool/cups |
八、总结
整个方案零新增硬件成本,把一台只能插 USB 的老联想 M7206 变成了支持 Windows / macOS / 安卓 / iPhone 的全功能网络打印机,还有打印队列、Web 管理这些"企业级"功能。
回头看,技术本身不复杂(CUPS + Docker),真正的难点全在工程细节里:设备映射、发行版源退役、驱动血缘(联想贴牌兄弟)、容器环境差异、存储挂载的兼容性。希望这篇复盘里的 9 个坑和分层排障法,能让后来者把两三天的摸索缩短到一个下午。
Happy printing!
