linuxserver/colibre-web
linuxserver/colibre-web 提供了易于浏览、阅读和狭隘现有 Calibre 数据库提供的电子书。我计划在 linuxserver/calibre容器 部署之后结合使用,以实现一个较为完善的电子书网站。
安装
采用Docker Compose是比较简单方便的方法
初步运行
创建数据目录:
mkdir -p ~/docs/calibre-suite/{calibre-config,web-config,library}
cd ~/docs/calibre-suite
编写
docker-compose.yml配置(该配置没有启用HTTPS,现在较新版本 Selkies(基于WebRTC视频流的桌面) 已经强制采用HTTPS,所以实际访问无法打开桌面,必须改进增加HTTPS ):
docker-compose.ymlservices:
# 后台转换/管理核心(替代本地客户端)
calibre-backend:
image: lscr.io/linuxserver/calibre:latest
container_name: calibre-backend
environment:
- PUID=1000
- PGID=1000
- TZ=Asia/Shanghai
volumes:
- ./calibre-config:/config
- ./library:/library # 关键:全量图书库和自动生成的 metadata.db 都在这里
ports:
- 8080:8080 # 访问 Calibre 虚拟桌面桌面(WebVNC)
restart: unless-stopped
# 精美前端与 Kobo 同步引擎
calibre-web:
image: lscr.io/linuxserver/calibre-web:latest
container_name: calibre-web
environment:
- PUID=1000
- PGID=1000
- TZ=Asia/Shanghai
volumes:
- ./web-config:/config
- ./library:/books # 关键:像素级对齐并读取同一个 library 目录
ports:
- 8083:8083 # 访问 Kobo 同步前端
depends_on:
- calibre-backend # 确保后端先启动并初始化数据库
restart: unless-stopped
改进HTTPS访问
由于 linuxserver/calibre容器 直接访问 http://<服务器IP>:8080 提示报错必须改进为HTTPS访问,所以上述Docker Compose再增加 Nginx 容器实现 NGINX反向代理https
创建数据目录:
mkdir -p ~/docs/calibre-suite/{calibre-config,web-config,library}
# 增加创建nginx目录
mkdir -p ~/docs/calibre-suite/nginx/{conf.d,certs}
使用OpenSSL创建自签名证书:
cd ~/docs/calibre-suite/nginx/certs
openssl req -x509 -nodes -days 3650 -newkey rsa:2048 \
-keyout server.key -out server.crt \
-subj "/C=CN/ST=Shanghai/L=Shanghai/O=Homelab/OU=Calibre/CN=cloud-atlas.dev"
完成以后 certs 目录就有了 server.crt (公钥) 和 server.key (私钥)
编辑一个 支持WebSokcet 的握手升级头(Upgrade Headers) 的NGINX配置,这样就能够支持Selkies/KasmVNC 平台底层深度依赖 WebSocket(WS) 协议进行超低延迟的桌面画面广播:
~/docs/calibre-suite/nginx/conf.d/calibre.conf
~/docs/calibre-suite/nginx/conf.d/calibre.confserver {
listen 443 ssl;
server_name localhost; # 内网通过 IP 访问,这里写 localhost 即可
# 像素级挂载刚才生成的自签名证书
ssl_certificate /etc/nginx/certs/server.crt;
ssl_certificate_key /etc/nginx/certs/server.key;
# 优化 SSL 安全参数
ssl_protocols TLSv1.2 TLSv1.3;
ssl_ciphers HIGH:!aNULL:!MD5;
# 1. 拦截并代理 Calibre 图形后端(WebVNC / Selkies)
location / {
proxy_pass http://calibre-backend:8080; # 利用 Docker 内置 DNS 直连后端容器端口
# 核心:必须向下透传真实的客户端物理 IP
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;
# 核心大杀器:完美支持 Selkies 的 WebRTC WebSocket 流量升级握手
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "Upgrade";
# 调大超时时间,防止看书或操作中断开链接
proxy_read_timeout 86400;
}
}
重新改写
docker-compose.yml:
services:
# 🔒 新增的 Nginx 高性能反向代理
nginx-proxy:
image: nginx:alpine
container_name: nginx-ingress
restart: unless-stopped
ports:
- "8080:8080" # 👈 暴露 8080 给后端管理界面 (HTTPS)
- "8083:8083" # 👈 暴露 8083 给前端 Web 和 Kobo 同步 (HTTPS)
- "80:80" # 👈 留着 80 做普通的 HTTP 跳转(可选)
networks:
- calibre-network
volumes:
- ./nginx/conf.d:/etc/nginx/conf.d:ro
- ./nginx/certs:/etc/nginx/certs:ro
depends_on:
- calibre-backend
- calibre-web
# 后台转换/管理核心(替代本地客户端)
calibre-backend:
image: lscr.io/linuxserver/calibre:latest
container_name: calibre-backend
restart: always
#privileged: true # 👈 关键:物理放开内核特权,允许容器在内部执行 mount/umount 挂载操作(不过这个权限过宽所以最终取消)
#
# **我现在取消 /dev/sda 映射进容器,改为在Host主机挂载sda到 /mnt/kobo ,然后映射目录,可避免容器高危权限
#ipc: host # 👈 核心修正 1:与宿主机共享 IPC 内存总线
#cap_add:
# - SYS_ADMIN # 👈 核心修正 2:允许容器在内部执行 mount/umount 挂载物理 Kobo 设备
# - SYS_RAWIO # 👈 核心修正 3: 允许容器直接对透传的 /dev/sda 块设备进行原始 I/O 读写
#devices:
# - "/dev/sda:/dev/sda" # 👈 核心:将物理主机的整个 Kobo 磁盘透传给容器内部
# ************
# ports: # 👈 注意:这里绝对不要再把 8080 映射到宿主机了!由 Nginx 独占宿主机 8080
expose:
- "8080"
networks:
- calibre-network
environment:
- PUID=1000
- PGID=1000
- TZ=Asia/Shanghai
# 🪐 核心注入 1:NVIDIA 全量可见性
- NVIDIA_VISIBLE_DEVICES=GPU-794d1de5-b8c7-9b49-6fe3-f96f8fd98a19 # 通过 nvidia-smi --query-gpu=index,name,uuid --format=csv 查询出GPU设备的uuid
- NVIDIA_DRIVER_CAPABILITIES=all,graphics,utility,video
# 🪐 核心注入 2:直接通知 Selkies 渲染节点切到 Tesla P10 对应的渲染端(通常是 renderD129)
- DRI_NODE=/dev/dri/renderD129
# 🪐 核心注入 3:调用 NVIDIA 官方 Runtime 渲染引擎
deploy:
resources:
reservations:
devices:
- driver: nvidia
# 🪐 核心修正 3:精准划拨设备,只将指定的 UUID 硬件资源注入容器空间
device_ids: ['GPU-794d1de5-b8c7-9b49-6fe3-f96f8fd98a19']
capabilities: [gpu, graphics, video]
volumes:
- ./calibre-config:/config
- ./library:/library # 关键:全量图书库和自动生成的 metadata.db 都在这里
- ./import:/import # 独立的书籍自动导入目录
- /mnt/kobo:/media/kobo:shared # 直接把宿主机已经挂载好的 Kobo 目录,映射进容器的 /media/kobo
# 精美前端与 Kobo 同步引擎
calibre-web:
image: lscr.io/linuxserver/calibre-web:latest
container_name: calibre-web
restart: always
# ports: # 👈 注意:这里绝对不要再把 8083 映射到宿主机了!由 Nginx 独占宿主机 8083
expose:
- "8083"
networks:
- calibre-network
environment:
- PUID=1000
- PGID=1000
- TZ=Asia/Shanghai
volumes:
- ./web-config:/config
- ./library:/books # 关键:像素级对齐并读取同一个 library 目录
depends_on:
- calibre-backend # 确保后端先启动并初始化数据库
networks:
calibre-network:
driver: bridge
备注
Compose设置了
depends_on确保启动顺序后端数据库自动生成:
calibre-backend启动一个完整的Calibre运行环境,在初始化时候将数据目录修改为/library目录下初始化metadata.db核心数据库文件,就能和 Calibre-Web 共享目录前端无缝读取: 访问
http://<服务器IP>:8083时WEB和后端共享物理目录,这样 Calibre-Web就能够直接访问后端生成的metadata.db特别增加一个
import目录,因为Calibre支持一种自动导入功能,只要将自动导入目录指向这个import目录,这样就能将文件从后台复制到该目录下,然后系统就会自动导入Calibre书库,不需要人工干预
备注
Calibre-Web 本质上是 Calibre 桌面端的 Web 前端,因此它的运行必须依赖一个由桌面端生成的初始化索引数据库文件 metadata.db 。如果没有按照上文同时运行 Calibre-Web 和 Calibre,那么可以从官方下载一个空白的 metadata.db 模板存放到 books 目录
wget https://raw.githubusercontent.com/janeczku/calibre-web/master/optional-calibre-mount/metadata.db -O ~/homelab/calibre-web/books/metadata.db
访问
https://<服务器IP>:8080就能看到一个Calibre桌面客户端软件;访问http://<你的服务器IP>:8083就能看到Calibre Web界面
备注
Calibre桌面 详见 linuxserver/calibre容器
再次改进HTTPS访问
我在实践 Calibre-WEB kobo同步 时发现,如果简单配置 api_endpoint 使用 http://YOUR_DOMAIN_OR_IP/kobo/xxxxxxxxxxxxxxxxx 似乎存在连接问题。gemini提示新版本kobo可能强制使用了https访问,所以需要调整上述配置,将 Calibre-backend 和 Calibre-web 都采用HTTPS。
备注
由于是两个服务,最优雅的方式是使用域名来区分,让nginx基于域名访问SNI来区分反向代理的目标。不过,我的homelab实际上还没有部署本地DNS,所以在这里暂时采用端口来区分不同服务。
警告
这段实践确实完成了HTTPS部署的 nginx-ingress ,部署方法可以作为后续迭代改进的参考。但是,依然没有解决 Kobo Libra H2O 访问自建Calibre书库的问题,因为我发现Kobo根本就没有访问NGINX服务。这里有一个推测是这个 Calibre-WEB kobo同步 同步功能需要先激Kobo电子阅读器的“书城”功能。但是,在中国大陆Kobo电子书尝试登录书城会提示所在地区不在服务范围(被官方屏蔽了),我参考gemini使用了 SQLite 强制插入数据库记录,但是看起来没有效果
docker-compose.yml:
services:
# 🔒 新增的 Nginx 高性能反向代理
nginx-proxy:
image: nginx:alpine
container_name: nginx-ingress
restart: unless-stopped
ports:
- "8080:8080" # 👈 暴露 8080 给后端管理界面 (HTTPS)
- "8083:8083" # 👈 暴露 8083 给前端 Web 和 Kobo 同步 (HTTPS)
- "80:80" # 👈 留着 80 做普通的 HTTP 跳转(可选)
networks:
- calibre-network
volumes:
- ./nginx/conf.d:/etc/nginx/conf.d:ro
- ./nginx/certs:/etc/nginx/certs:ro
depends_on:
- calibre-backend
- calibre-web
# 后台转换/管理核心(替代本地客户端)
calibre-backend:
image: lscr.io/linuxserver/calibre:latest
container_name: calibre-backend
restart: always
#privileged: true # 👈 关键:物理放开内核特权,允许容器在内部执行 mount/umount 挂载操作(不过这个权限过宽所以最终取消)
#
# **我现在取消 /dev/sda 映射进容器,改为在Host主机挂载sda到 /mnt/kobo ,然后映射目录,可避免容器高危权限
#ipc: host # 👈 核心修正 1:与宿主机共享 IPC 内存总线
#cap_add:
# - SYS_ADMIN # 👈 核心修正 2:允许容器在内部执行 mount/umount 挂载物理 Kobo 设备
# - SYS_RAWIO # 👈 核心修正 3: 允许容器直接对透传的 /dev/sda 块设备进行原始 I/O 读写
#devices:
# - "/dev/sda:/dev/sda" # 👈 核心:将物理主机的整个 Kobo 磁盘透传给容器内部
# ************
# ports: # 👈 注意:这里绝对不要再把 8080 映射到宿主机了!由 Nginx 独占宿主机 8080
expose:
- "8080"
networks:
- calibre-network
environment:
- PUID=1000
- PGID=1000
- TZ=Asia/Shanghai
# 🪐 核心注入 1:NVIDIA 全量可见性
- NVIDIA_VISIBLE_DEVICES=GPU-794d1de5-b8c7-9b49-6fe3-f96f8fd98a19 # 通过 nvidia-smi --query-gpu=index,name,uuid --format=csv 查询出GPU设备的uuid
- NVIDIA_DRIVER_CAPABILITIES=all,graphics,utility,video
# 🪐 核心注入 2:直接通知 Selkies 渲染节点切到 Tesla P10 对应的渲染端(通常是 renderD129)
- DRI_NODE=/dev/dri/renderD129
# 🪐 核心注入 3:调用 NVIDIA 官方 Runtime 渲染引擎
deploy:
resources:
reservations:
devices:
- driver: nvidia
# 🪐 核心修正 3:精准划拨设备,只将指定的 UUID 硬件资源注入容器空间
device_ids: ['GPU-794d1de5-b8c7-9b49-6fe3-f96f8fd98a19']
capabilities: [gpu, graphics, video]
volumes:
- ./calibre-config:/config
- ./library:/library # 关键:全量图书库和自动生成的 metadata.db 都在这里
- ./import:/import # 独立的书籍自动导入目录
- /mnt/kobo:/media/kobo:shared # 直接把宿主机已经挂载好的 Kobo 目录,映射进容器的 /media/kobo
# 精美前端与 Kobo 同步引擎
calibre-web:
image: lscr.io/linuxserver/calibre-web:latest
container_name: calibre-web
restart: always
# ports: # 👈 注意:这里绝对不要再把 8083 映射到宿主机了!由 Nginx 独占宿主机 8083
expose:
- "8083"
networks:
- calibre-network
environment:
- PUID=1000
- PGID=1000
- TZ=Asia/Shanghai
volumes:
- ./web-config:/config
- ./library:/books # 关键:像素级对齐并读取同一个 library 目录
depends_on:
- calibre-backend # 确保后端先启动并初始化数据库
networks:
calibre-network:
driver: bridge
nginx/conf.d/calibre.conf:
# ========================================================
# 1. 管理后端控制面:https://192.168.1.9:8080
# ========================================================
server {
listen 8080 ssl default_server;
listen [::]:8080 ssl default_server;
http2 on; # 新版标准:显式开启 HTTP/2
server_name _; # 👈 放弃域名匹配,全量接收 8080 端口流量
# 像素级挂载刚才生成的自签名证书
ssl_certificate /etc/nginx/certs/server.crt;
ssl_certificate_key /etc/nginx/certs/server.key;
# 优化 SSL 安全参数
ssl_protocols TLSv1.2 TLSv1.3;
ssl_ciphers HIGH:!aNULL:!MD5;
ssl_prefer_server_ciphers on; # 👈 由服务器端决定加密方法,提高安全性
client_max_body_size 500M; # 加大客户端上传文件大小限制
# 1. 拦截并代理 Calibre 图形后端(WebVNC / Selkies)
http2_recv_timeout 5s; # 👈 启用 HTTP/2 层的流超时控制, 如果 5 秒内没有收到客户端的有效帧,强制关闭该 HTTP/2 流
location / {
proxy_pass http://calibre-backend:8080; # 利用 Docker 内置 DNS 直连后端容器端口
# 启用 TCP 探测,一旦容器突发崩溃,网关立刻感知并向上游断开
proxy_next_upstream error timeout invalid_header http_500 http_502;
proxy_next_upstream_timeout 2s;
proxy_next_upstream_tries 2;
# 关键:当后端 Crash 时,Nginx 主动向浏览器返回 502,并强制附带 Connection: close 头部
# 这会迫使 Safari/Chrome 的网络内核瞬间物理销毁这条 HTTP/2 TCP 长连接,阻止其继续套用旧帧 ID
error_page 502 503 504 = @error_handle;
# 核心:必须向下透传真实的客户端物理 IP
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;
# 核心大杀器:完美支持 Selkies 的 WebRTC WebSocket 流量升级握手
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "Upgrade";
# 调大超时时间,防止看书或操作中断开链接
proxy_read_timeout 86400;
}
location @error_handle {
add_header Connection "close" always;
add_header Cache-Control "no-cache, no-store, must-revalidate" always;
return 502 "Gateway Error: Backend instance reset.";
}
}
# ========================================================
# 2. 前端网页与 Kobo 同步面:https://192.168.1.9:8083
# ========================================================
server {
listen 8083 ssl http2 default_server;
listen [::]:8083 ssl http2 default_server;
http2 on; # 新版标准:显式开启 HTTP/2
server_name _; # 👈 放弃域名匹配,全量接收 8083 端口流量
ssl_certificate /etc/nginx/certs/server.crt;
ssl_certificate_key /etc/nginx/certs/server.key;
ssl_protocols TLSv1.2 TLSv1.3;
ssl_ciphers HIGH:!aNULL:!MD5;
ssl_prefer_server_ciphers on;
client_max_body_size 500M;
location / {
proxy_pass http://calibre-web:8083; # 👈 转发给前台网络服务容器内部端口
proxy_set_header Host $http_host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
# 核心:必须透传 HTTPS 状态,否则 Kobo 同步包内的路径会解析错误
proxy_set_header X-Scheme $scheme;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_buffering off;
}
}
需要注意上述配置中的一些调整点:
单独建立了一个
calibre-network的bridge网络,这个网络仅用于连接nginx-ingress/calibre-backend/calibre-web,可以隔离其他容器的网络如果以后要和其他容器网络互通,则可以调整
dirver: bridge改为driver: overlay或Macvlan直接分配的物理局域网IP这种 显式声明网络 可以配置为
external: true(外部预建网络),这样网络就便横一个常驻的内网总线,即使容器销毁,网络依然留存,随时等待新容器挂载独立网络默认随机分配IP网段(例如
172.17.0.0/16),通常和局域网网段有一定概率重合。这种显式声明网络可以独立配置指定的内网私有子网,防止网段冲图:
networks:
calibre-network:
driver: bridge
ipam:
config:
- subnet: 172.28.5.0/24 # 👈 像素级规避你家里 192.168.1.x 的物理网段
只有
nginx-ingress通过ports:配置映射了Host主机的端口,也就是只有这个容器对外提供服务,并通过反向代理访问nginx-backend和calibre-webrestart: always方式确保容器始终启动,避免偶然stop之后没有随着操作系统启动而启动
在nginx配置中有如下调整:
proxy_set_header Host $http_host;取代了proxy_set_header Host $host;:在绝大多数情况下的上述两个配置行为是一样的,但在非标准端口(比如当前使用的 8080 和 8083)的物理拓扑下,它们有着决定性的区别:
$host(不带端口):只包含客户端请求的域名或 IP 地址。例如客户端访问 https://192.168.1.9:8083/ ,Nginx 传给后端的 Host 头部纯粹是 192.168.1.9。
$http_host(带端口):直接原封不动地透传客户端浏览器发来的 Host 请求头,包含物理端口号。例如传递过去的是 192.168.1.9:8083。
这里改成
$http_host非常关键,因为如果继续使用$host则反向代理只传递主机信息,Calibre-web收到的请求只会看到访问 192.168.1.9 ,就会以为自己运行在标准的443端口,从而生成类似 https://192.168.1.9/v2/synctoken... 这样的错误下载连接(漏掉了:8083),这会导致客户端下载报错哦: 非标准端口的反向代理拓扑中,必须使用 $http_host 这样才能提供端口号
需要注意上述配置中的经验教训 一次异常排查:HTTP2多路复用(Multiplexig)/长连接和本地Safari浏览器缓存冲突 ,一定要在NGINX中配置后端崩溃时主动向前端浏览器发送连接重置和清理通道,否则后端crash后浏览器缓存的TLS通道状态机会导致无法连接服务
使用
访问 http://<你的服务器IP>:8083 初次默认管理员账号:admin ,默认初始密码: admin123 务必第一时间进入设置修改默认密码。