基于 CUPS 的网页版打印管理工具。通过浏览器上传文件、远程提交打印任务,支持多用户管理与打印记录追踪,适合家庭和小型办公室使用。
- 多格式支持:PDF、图片(JPG/PNG/GIF/HEIC)、Office 文档(doc/docx/xls/xlsx/ppt/pptx)、OFD、纯文本
- 自动转换:Office 文档通过 LibreOffice 转 PDF;OFD 通过内置 Java 转换器(基于 ofdrw)转 PDF;文本/图片在服务端渲染为 PDF
- 多图片合并打印:一次选择多张图片自动合并为一份 PDF
- 打印选项:份数、单双面、彩色/黑白、纸张大小、纸张类型、页面方向、页码范围、缩放、镜像打印
- 实时预览:支持 PDF 预览、纸张方向的可视化预览、页数估算
镜像内预装了 Debian printer-driver-all 等通用驱动包,覆盖大部分常见打印机。对于特定品牌打印机,提供按需手动安装的驱动脚本和 Web 管理界面:
预装通用驱动(开箱即用):
printer-driver-all:Debian 维护的驱动 meta 包,包含 splix、c2050、m2300w、ptouch 等printer-driver-cups-pdf:虚拟 PDF 打印机printer-driver-escpr:Epson ESC/P-R 标准款(大部分 Epson 喷墨老机型)printer-driver-foo2zjs:ZjStream / Hiperc / OAKT 协议(部分 HP / Konica / Minolta 老款激光机)printer-driver-brlaser:Brother 老款激光机foomatic-db-compressed-ppds+openprinting-ppds:海量 PPD 库hplip+hpijs-ppds+hp-ppd:HP 全系打印套件ipp-usb+ CUPS 内置 driverless:IPP Everywhere / AirPrint / Mopria 自动识别
可选厂商驱动(通过 Web 界面或命令行按需安装,安装后自动持久化):
| 驱动 | 命令名 | 架构 | 适用机型 |
|---|---|---|---|
| Canon UFR II | canon-ufr2 |
amd64 / arm64 | i-SENSYS LBP/MF、imageCLASS、imageRUNNER 等 |
| Canon CAPT | canon-capt |
全架构 🔧 | LBP2900 / LBP2900B |
| HP LaserJet 1020 固件 | hp-laserjet1020 |
全架构 | HP LaserJet 1020 / 1020 Plus |
| HP foo2zjs 固件 | foo2zjs-firmware |
全架构 🔧 | HP LaserJet 1000/1005/1018/P1005/P1006/P1505 |
| Epson ESC/P-R 2 | escpr2 |
amd64 / armhf / arm64 | ET-18100, L8050, L8160, WF-7840 等 |
| Epson 国行驱动 | epson-cn |
仅 amd64 | L380, L455 等国行机型 |
| Konica Minolta bizhub | konica-bizhub |
amd64 / arm64 | bizhub 3000MF |
| Sharp PostScript | sharp |
全架构 | MX-C2622R 等 PostScript 打印机 |
| Gutenprint | gutenprint |
amd64 / arm64 | 大量 Epson/Canon/HP 老机型 |
🔧 = 需要在容器内现场编译,耗时较长(几分钟到十几分钟,ARM 小主机更慢)。其他驱动是下载 + 解包安装,通常几十秒内完成。
Epson ESC/P-R 2 在 amd64 / armhf 上直接安装预编译包,在 arm64 上会回退到源码编译,也需要几分钟。
「架构」列就是实际的硬限制:厂商没有提供对应架构的二进制时,Web 界面会把该驱动的「安装」按钮禁用并提示原因,不会让你点一个必然失败的按钮。
驱动管理页面仅管理员可见(登录后导航栏的「驱动」入口):
- 自动检测:扫描 USB / 网络打印机,自动匹配推荐驱动
- 一键安装:检测到打印机后一键安装驱动,并自动
lpadmin添加到 CUPS(默认纸张设为 A4) - 驱动列表:查看所有可用驱动的安装状态、安装时间与支持架构,一键安装 / 卸载
- 上传自定义驱动:支持上传 PPD 文件(
.ppd)或 Debian 包(.deb),仅这两种扩展名 - 驱动持久化:安装的驱动文件自动快照到
.drivers持久卷,容器重建后自动恢复
点击「安装」后接口会立刻返回,真正的安装在后台执行,页面上会出现一个进度卡片,每 2 秒刷新一次并实时展示编译 / 安装日志:
- 需要编译的驱动(Canon CAPT、HP foo2zjs 固件、arm64 上的 Epson ESC/P-R 2)可能要几分钟到十几分钟,页面一直显示滚动日志是正常的,不是卡住了
- 这期间请不要刷新页面、不要重复点击。刷新虽然不会中断后台安装,但页面上的实时日志会丢失,只能改用
docker compose logs -f cups观察 - 后台任务的硬超时是 30 分钟,页面等待上限 35 分钟
apt / dpkg 本身持有全局锁,并发安装只会互相失败。因此后端限制同时只允许一个驱动任务(安装 / 卸载 / 一键安装并添加),任务进行中再发起第二个会被直接拒绝,并提示「已有驱动任务正在执行,请等待其完成后重试」;有任务在跑时上传 .deb 也会被同样拒绝。
| 上传类型 | 容器重启后 |
|---|---|
.ppd |
✅ 自动恢复(文件被快照到 .drivers,启动时还原到 /usr/share/cups/model/custom) |
.deb |
❌ 需要重新上传安装(只做归档记录,界面上会列出装过哪些包并提示重装) |
原因是 .deb 的真正安装动作发生在包内的安装脚本里,光把文件拷回来并不能让驱动生效。界面上的提示原文是:「上传的 .deb 包不会随容器重启自动恢复,重启后需要重新上传安装。」
⚠️ 上传.deb的安全风险:安装.deb时 dpkg 会以 root 身份执行包内的安装脚本,等价于在容器里执行任意代码,并且会改动容器的系统状态。这是有意保留给管理员的能力,但也意味着管理员账号密码等同于容器的 root 凭据。请只上传来源可信的.deb,并且不要在不可信的多人环境下开放管理员账号(普通user角色看不到也调不到驱动接口)。每次上传都会把上传者用户名写进容器日志,便于事后审计。
⚠️ 驱动持久化依赖./.drivers卷:手动安装的第三方驱动全部快照在这个目录里。删掉它(或忘记挂这个卷)= 丢失所有手动安装的驱动,重启后需要在「驱动」页面重新装一遍。备份时别漏了它。
也可以通过命令行安装驱动(命令行是同步执行的,会一直占用终端直到结束):
# 查看可用驱动
docker exec cups driver-list
# 安装驱动
docker exec cups driver-install canon-ufr2
# 查看已安装驱动
docker exec cups driver-list --installed
# 卸载驱动
docker exec cups driver-remove canon-ufr2- 多用户系统:支持
admin/user两种角色 - 默认管理员:首次启动自动创建
admin/admin,admin账号受保护无法被删除或重命名 - 打印记录:完整保存每次打印的文件、页数、份数、双面/彩色选项、状态等
- 用户管理:创建、编辑、删除用户;修改角色与联系信息
- 打印记录查询:可按用户名、时间范围过滤
- 数据保留策略:按天数自动清理过期打印记录和对应文件(每小时巡检一次)
- Session 认证:基于 Gorilla
securecookie(加密 + 签名),密钥自动生成并持久化到数据库 - CSRF 防护:对所有非 GET/HEAD/OPTIONS 请求校验
X-CSRF-Token - 密码安全:bcrypt 加密存储
- 后端:Go 1.26 · Gorilla Mux · SQLite(
modernc.org/sqlite,纯 Go 实现,无需 CGO) - 打印协议:OpenPrinting/goipp(IPP)
- 前端:Vue 3 · Vite 7 · Nuxt UI v4 · Tailwind CSS v4 · Vue Router(hash 模式)
- 文档转换:LibreOffice(Office → PDF)· ofdrw(OFD → PDF,Java 21)
- 打印服务:CUPS(源码编译 2.4.x,覆盖 apt 版本)
提供两种部署方式:
- Docker 与 Docker Compose
- USB 打印机(若使用本地打印机)
services:
cups:
image: hanxi/cups-web:latest
container_name: cups
user: root
security_opt:
- apparmor:unconfined
environment:
- CUPSADMIN=${CUPSADMIN:-print}
- CUPSPASSWORD=${CUPSPASSWORD:-print}
- TZ=${TZ:-Asia/Shanghai}
ports:
- "631:631"
- "1180:8080"
volumes:
- ./.etc:/etc/cups
- ./.data:/data
- ./.uploads:/uploads
- ./.drivers:/opt/cups-drivers/data
- /dev/bus/usb:/dev/bus/usb
- /run/udev:/run/udev:ro
device_cgroup_rules:
- 'c 189:* rmw'
restart: unless-stopped也可直接下载仓库内的 docker-compose.yml:
wget https://raw.githubusercontent.com/hanxi/cups-web/master/docker-compose.yml在同目录创建 .env:
CUPSADMIN=print
CUPSPASSWORD=your_cups_password
TZ=Asia/Shanghaidocker compose up -d大部分打印机靠镜像预装的通用驱动即可直接使用,只有特定品牌机型才需要这一步。
通过 Web 界面安装(推荐):
- 访问 http://localhost:1180,使用
admin/admin登录 - 点击导航栏「驱动」进入驱动管理页面(仅管理员可见)
- 点击「扫描打印机」自动检测已连接的打印机
- 对检测到的打印机点击「一键安装并添加」
⏳ 安装是后台异步执行的,页面上会实时滚动安装日志。需要编译的驱动可能要十几分钟,请不要刷新页面或重复点击;同一时刻只能有一个驱动任务在跑。详见 驱动管理(Web 界面)。
或通过命令行安装:
docker exec cups driver-install canon-ufr2如果没有通过上面的自动检测添加打印机,也可以手动配置:
访问 CUPS 管理界面:http://localhost:631,使用 CUPS 管理员账号登录并添加打印机。
⚠️ 重要:添加打印机后,必须在 CUPS 管理后台将其设为 Shared(共享) 状态,否则 Web 端无法发现该打印机。
浏览器打开 http://localhost:1180,使用默认账号登录:
- 用户名:
admin - 密码:
admin
⚠️ 首次登录请立即修改默认密码。
适合已有 CUPS 服务的场景。
⚠️ 裸二进制部署的能力边界:二进制里只有 Web 服务本身,CUPS 需要你自己在宿主机上安装并配置好(含打印机驱动)。镜像特有的功能在这里都不可用:
- 驱动管理页面用不了:驱动的安装 / 卸载脚本(
driver-install等)和持久化目录/opt/cups-drivers是镜像内置的。裸二进制环境下这些文件不存在,「驱动」页面里所有驱动都会显示为未安装、「安装」按钮被禁用并提示「当前镜像缺少该驱动的安装脚本」;如果强行调用接口,任务会以no such file or directory失败。请直接用宿主机的包管理器(apt install printer-driver-*)或 CUPS 管理界面装驱动。- 自动检测打印机依赖
lpinfo(cups-client包),缺失时「扫描打印机」会报failed to detect printers。- Office / OFD / PDF 标准化依赖 LibreOffice、Java、Ghostscript,需要自行安装(见下文)。
从 GitHub Releases 下载对应平台的二进制:
| 平台 | 架构 | 文件名 |
|---|---|---|
| Linux | amd64 | cups-web-linux-amd64 |
| Linux | arm64 | cups-web-linux-arm64 |
| Linux | armv7 | cups-web-linux-armv7 |
| Linux | loong64 | cups-web-linux-loong64 |
| macOS | amd64 | cups-web-darwin-amd64 |
| macOS | arm64 | cups-web-darwin-arm64 |
| Windows | amd64 | cups-web-windows-amd64.exe |
wget https://github.com/hanxi/cups-web/releases/latest/download/cups-web-linux-amd64
chmod +x cups-web-linux-amd64export CUPS_HOST=localhost:631
export DB_PATH=./data/cups-web.db
export UPLOAD_DIR=./uploads
export LISTEN_ADDR=:8080
./cups-web-linux-amd64或使用命令行参数(优先级高于环境变量):
./cups-web-linux-amd64 -addr :8080
⚠️ OFD 打印仅在 Docker 镜像中开箱即用。二进制部署若需支持 OFD,需要另行安装 Java 运行时(镜像内为 Java 21)并把ofd-converter.jar放到/ofd-converter.jar(或手动改源码中的路径)。
浏览器打开 http://localhost:8080,使用 admin/admin 登录。
| 变量名 | 说明 | 默认值 |
|---|---|---|
LISTEN_ADDR |
Web 服务监听地址 | :8080 |
DB_PATH |
SQLite 数据库路径 | data/cups-web.db |
UPLOAD_DIR |
上传文件目录 | uploads |
CUPS_HOST |
CUPS 服务地址(Docker 内默认 localhost) |
localhost |
CUPSADMIN |
CUPS 管理员用户名 | print |
CUPSPASSWORD |
CUPS 管理员密码 | print |
TZ |
时区 | Asia/Shanghai |
💡
.env.example只列了 Docker 部署常用的三个:CUPSADMIN/CUPSPASSWORD/TZ。这三个在镜像里都已有内置默认值(Asia/Shanghai),不写.env也能启动。💡
DB_PATH/UPLOAD_DIR的默认值是相对路径,镜像内工作目录是/,所以容器里实际落在/data/cups-web.db与/uploads/,正好对应下面的卷映射,通常不需要显式设置。💡
CUPS_HOST单容器化后默认就是localhost(cupsd 与 Web 同容器),一般无需设置;只有把 Web 指向另一台机器上的 CUPS 时才需要,可写host或host:port(省略端口时自动补631)。
| 参数 | 说明 |
|---|---|
-addr |
监听地址,优先级高于 LISTEN_ADDR |
- CUPS:
631(管理界面 + IPP 协议) - Web:容器内
8080,docker-compose.yml默认映射到宿主机1180
Docker 默认卷映射:
| 宿主机路径 | 容器路径 | 说明 |
|---|---|---|
./.etc |
/etc/cups |
CUPS 配置(打印机、PPD 等) |
./.data |
/data |
cups-web 数据库 |
./.uploads |
/uploads |
上传的原始文件与转换后 PDF |
./.drivers |
/opt/cups-drivers/data |
手动安装的打印机驱动快照( |
此外还有两个非数据类的挂载,用于 USB 打印机识别与热插拔:
| 宿主机路径 | 容器路径 | 说明 |
|---|---|---|
/dev/bus/usb |
/dev/bus/usb |
以目录方式挂载(而不是 devices:),这样打印机后开机时新建的设备节点能实时传播进容器 |
/run/udev |
/run/udev(只读) |
让 libusb 读到设备属性,改善识别;宿主机没有该目录时可以删掉这一行 |
| 选项 | 为什么需要 |
|---|---|
user: root |
容器内要运行 cupsd、lpadmin、dpkg(驱动安装),还要往 /usr/lib/cups、/usr/share/ppd 等系统路径写驱动文件 |
security_opt: [apparmor:unconfined] |
解除 AppArmor 限制(Issue #91)。PVE (Proxmox VE) LXC 等环境下会出现 apparmor="DENIED" 导致打印失败;单容器化后它同时也保护 LibreOffice / OFD 转换子进程不被拦截 |
device_cgroup_rules: ['c 189:* rmw'] |
放开 USB 字符设备(major 189)的 cgroup 权限,配合 /dev/bus/usb 目录挂载实现 USB 打印机热插拔(Issue #81)。若你的 Docker 环境不支持该字段,改用 privileged: true |
| 类型 | 扩展名 | 处理方式 |
|---|---|---|
.pdf |
直接打印 | |
| 图片 | .jpg .jpeg .png .gif .heic |
转换为 PDF(支持多张合并) |
| Office | .doc .docx .xls .xlsx .ppt .pptx |
通过 LibreOffice 转换 |
| OFD | .ofd |
通过 ofdrw 转换 |
| 文本 | .txt .md .html |
服务端渲染为 PDF |
- 选择打印机
- 上传文件(支持多图)
- 预览转换后的 PDF、调整打印参数
- 确认提交,系统自动落库并下发到 CUPS
- 用户管理:创建、编辑、删除;默认
admin账号不可删除、不可改名、角色固定 - 打印记录:查看全站记录,按用户名/日期过滤,下载原始文件
- 系统设置:数据保留天数(
0表示永久保留) - 驱动管理:自动检测打印机、安装/卸载驱动、上传自定义 PPD/deb(后台异步执行 + 实时日志,同时只跑一个任务)
通过反向代理(例如 Nginx)提供 HTTPS:
server {
listen 443 ssl;
server_name example.com;
ssl_certificate /path/to/cert.pem;
ssl_certificate_key /path/to/key.pem;
location / {
proxy_pass http://localhost:1180;
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;
}
}编辑 docker-compose.yml:
services:
cups:
ports:
- "你的CUPS端口:631"
- "你的Web端口:8080"cp ./.data/cups-web.db /backup/location/
tar -czf uploads-backup.tar.gz ./.uploads/
tar -czf cups-config-backup.tar.gz ./.etc/
tar -czf drivers-backup.tar.gz ./.drivers/💡
.drivers一定要一起备份——它是所有手动安装的第三方驱动的唯一副本。注意它是按架构快照的:把 amd64 上备份的.drivers恢复到 arm64 机器上,驱动列表会提示「安装于 amd64,与当前架构不符,建议卸载重装」。
删除数据库文件后重启即可重置为默认 admin/admin(会丢失全部数据):
docker compose down
rm ./.data/cups-web.db
docker compose up -d- 检查打印机是否在 CUPS 中正常列出(http://localhost:631)
- 确认打印机设置为 Shared
- 重启容器:
docker compose restart cups
使用最新的 docker-compose.yml(volume 目录挂载 /dev/bus/usb + device_cgroup_rules)即可支持热插拔。若你的 Docker 环境不支持 device_cgroup_rules,改用 privileged: true 即可。
确认 docker-compose.yml 中已配置驱动持久化卷:
volumes:
- ./.drivers:/opt/cups-drivers/data驱动数据保存在 .drivers 目录中,容器重建后自动恢复。删掉该目录就等于丢失全部手动安装的驱动,需要在「驱动」页面重新安装。
另外,通过「上传自定义驱动」装的 .deb 包本身不会自动恢复(只有 .ppd 会),容器重启后需要重新上传安装一次,界面上会列出装过哪些包作为提醒。
大概率没有。安装是后台异步执行的,页面上的进度卡片每 2 秒刷新一次并实时显示日志:
- 需要编译的驱动(Canon CAPT、HP foo2zjs 固件、arm64 上的 Epson ESC/P-R 2)在 ARM 小主机上十几分钟都属正常
- 只要日志还在增长就说明在正常推进;不要刷新页面或重复点击
- 如果不放心,另开终端看容器日志:
docker compose logs -f cups - 后台任务硬超时 30 分钟,页面等待上限 35 分钟;真的超时会明确报错
apt / dpkg 有全局锁,所以同一时刻只允许一个驱动任务(安装 / 卸载 / 一键安装并添加);有任务在跑时上传 .deb 也会被拒绝。等当前任务在页面上显示完成后再操作即可。
两种原因,鼠标悬停在按钮上会显示具体提示:
- 当前架构 xxx 不支持:厂商没有提供该 CPU 架构的二进制(例如 Epson 国行驱动仅 amd64、Canon UFR II 仅 amd64/arm64、Gutenprint 在 armhf 上无包)。请改用预装的通用驱动或 driverless/IPP Everywhere
- 当前镜像缺少该驱动的安装脚本:通常出现在裸二进制部署下(驱动脚本是镜像内置的),或使用了裁剪过的镜像
在服务中添加 security_opt: [apparmor:unconfined](最新 docker-compose.yml 已包含)。PVE LXC 容器还需开启 Nesting 和 Keyctl 功能。
- 转换有 60 秒超时,复杂文档可能超时
- 确认文档本身未损坏;可尝试本地先另存为 PDF 再上传
- 查看日志:
docker compose logs -f cups
docker compose logs -f cups欢迎提 Issue 和 Pull Request。开发者文档请参阅 AGENTS.md。
如果这个项目对你有帮助,欢迎通过以下方式支持:
点击右上角的 ⭐ Star 按钮,让更多人发现这个项目。
- 💝 爱发电 — 持续支持项目发展
- 扫码请作者喝杯奶茶 ☕
感谢你的支持!❤️
本项目采用 MIT 许可证,详见 LICENSE。




