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.yml
services:
  # 后台转换/管理核心(替代本地客户端)
  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

  • 创建数据目录:

创建数据目录,增加nginx配置目录
mkdir -p ~/docs/calibre-suite/{calibre-config,web-config,library}

# 增加创建nginx目录
mkdir -p ~/docs/calibre-suite/nginx/{conf.d,certs}
  • 使用OpenSSL创建自签名证书:

使用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.conf
server {
    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 :

增加NGINX反向代理HTTPS的容器的 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 目录

下载空白的metadata.db
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 :

调整https反向代理后端不同端口的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 :

调整2个服务反向代理nginx配置 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-backendcalibre-web

  • restart: 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 务必第一时间进入设置修改默认密码。

参考