Ubuntu 24.04 安装 Jelu 0.88.2 指南
在 Ubuntu 24.04 上从零安装 Jelu 0.88.2 自托管阅读追踪器:官方 jar 下载与 sha256 校验、systemd 托管、JVM 内存限定、离线安装备选方案、管理员初始化与四类启动故障排障实录,全部命令实测。
Ubuntu 24.04 安装 Jelu 0.88.2 指南
目标:在 Ubuntu 24.04 服务器上从零安装并以 systemd 托管 Jelu 0.88.2(自托管阅读追踪器,Spring Boot fat jar 形态),覆盖「官方 jar 在线安装」与「断网内网离线安装」两套方案、管理员账户初始化、JVM 内存限定与启动故障排障实录;全部命令已在 Ubuntu 24.04.5 LTS + OpenJDK 21.0.12.1 + Jelu v0.88.2 环境逐条实测。组件定位与选型背景见《《Jelu 介绍与选型指南》》。
一、环境信息
本文所有命令与回显均来自以下实测环境:
- 操作系统:Ubuntu 24.04.5 LTS(x86_64,
/etc/os-release中PRETTY_NAME="Ubuntu 24.04.5 LTS"); - Java 运行时:OpenJDK 21.0.12.1(
openjdk-21-jre-headless,apt 官方源版本 21.0.12.1+1-1~24.04.4); - Jelu 版本:v0.88.2(2026-10-07 发布,查询日期 2026-10-28;jar 资产
jelu-0.88.2.jar,108,216,402 字节); - 服务管理:systemd,单元名
jelu.service(enabled,开机自启); - 监听端口:11111(Jelu 默认端口),可选 nginx 1.24.0 反向代理 80 端口;
- 安装路径:jar 存放
/opt/jelu/,数据与工作目录/var/lib/jelu/; - 运行账户:专用系统用户
jelu(实测 uid/gid 997,shell 为/usr/sbin/nologin); - JVM 内存:
-Xms256m -Xmx512m(限定在 256–512MB 区间,见 4.7 节与 7.2 节); - 可选依赖:calibre(提供
fetch-ebook-metadata元数据抓取工具)、nginx(反向代理)。
二、前置条件
2.1 Java 运行时(必需)
Jelu 是 Spring Boot 应用,官方安装文档(bayang.github.io/jelu-web/installation/jar/)要求「Jelu needs at least Java 11」。本文选用 Ubuntu 24.04 官方源自带的 OpenJDK 21(实测通过):
sudo apt-get update
sudo apt-get install -y openjdk-21-jre-headless
已安装环境下重复执行上述安装命令的实测回显(输出节选):
Reading state information...
openjdk-21-jre-headless is already the newest version (21.0.12.1+1-1~24.04.4).
0 upgraded, 0 newly installed, 0 to remove and 1 not upgraded.
验证安装结果:
java -version
实测回显:
openjdk version "21.0.12.1" 2026-08-18
OpenJDK Runtime Environment (build 21.0.12.1+1-1-24.04.4-Ubuntu)
OpenJDK 64-Bit Server VM (build 21.0.12.1+1-1-24.04.4-Ubuntu, mixed mode, sharing)
💡 技术拓展:Ubuntu 源里的 OpenJDK 包有三种常见形态——
openjdk-21-jre-headless(无图形依赖、体积最小的运行时,服务器首选)、openjdk-21-jre(含 AWT/Swing 图形栈,桌面场景用)、openjdk-21-jdk(含 javac 等编译工具,开发场景用)。Jelu 只需运行 jar,用 jre-headless 即可;本文实测采用openjdk-21-jre-headless。
2.2 硬件资源
- 内存:JVM 堆限定 256–512MB;实测实例(512MB 堆)常驻内存 477.2M(
systemctl status jelu回显,见 4.9 节),1GB 内存的服务器可稳定运行; - 磁盘:jar 本体约 103MB;若安装 calibre(可选,元数据抓取用)连同依赖约占 1GB;数据库为 SQLite 单文件,实测录入少量书目时
jelu.db仅 278KB,随书目量增长。
2.3 网络与下载渠道
⚠️ 网络说明(实测):Jelu 官方发布渠道只有 GitHub Releases(github.com/bayang/jelu/releases),无国内官方镜像站。查询日期 2026-10-28 实测直连 GitHub 下载该 jar 限速严重(约 2 分钟仅完成 1.9MB),最终经 HTTP 代理完成完整下载;部署环境若有可用代理,用
export http_proxy=http://代理主机:端口 https_proxy=http://代理主机:端口会话级注入或curl -x http://代理主机:端口 -Lo jelu-0.88.2.jar <同一 URL>单次指定。无论走哪个渠道,务必以 4.4 节的 sha256 校验收口。
2.4 可选组件
- calibre(推荐):提供
fetch-ebook-metadata命令行工具,是 Jelu 默认的元数据抓取提供方:
sudo apt-get install -y calibre # 连同依赖体积较大,耐心等待
which fetch-ebook-metadata
实测回显:
/usr/bin/fetch-ebook-metadata
- nginx(可选):反向代理与后续加 HTTPS 的入口,安装与配置见 7.3 节。
三、版本选型说明
Jelu 发布节奏较快,查询日期 2026-10-28 实查 GitHub Releases 的近期版本如下:
| 版本 | 发布日期 | 要点 |
|---|---|---|
| v0.87.4 | 2026-10-05 | 常规修复 |
| v0.87.5 | 2026-10-05 | 常规修复 |
| v0.88.0 | 2026-10-05 | SQLite 引入 journalMode 配置(delete/wal),调优 busy_timeout 与连接池 |
| v0.88.1 | 2026-10-05 | 修复封面抓取在写事务外执行 |
| v0.88.2 | 2026-10-07 | 修复添加书目页 CSS、Goodreads ISBN 格式不一致、设置进度时更新事件 |
选型结论:新装直接用最新稳定版 v0.88.2(查询日期 2026-10-28 时 GitHub Releases 的 latest 即 v0.88.2)。本文全部步骤以 v0.88.2 为准,安装资产与校验值:
- 资产:
jelu-0.88.2.jar,108,216,402 字节; - sha256:
1d063355fbde5e75dcc67f1a700cc113aa3f3f00b80e69b299c9cd682d4cb085(实测sha256sum与发布页摘要一致,见 4.4 节)。
⚠️ 注意:0.88.x 三天连发 5 版,节奏较快;升级前先备份数据库(7.4 节),升级与回滚流程见第八章。
四、方案 A:官方 jar 在线安装 + systemd 托管(推荐)
4.1 方案说明
官方 Releases 直接发布 Spring Boot fat jar,单进程 java -jar 即可运行,是官方文档推荐、也是自托管服务器最常用的形态。本章流程:安装 Java → 创建运行用户与目录 → 下载并校验 jar → 前台试跑(Liquibase 自动建库)→ 写外置配置 → 编写 systemd 单元 → 启动自启 → 验证 → 创建管理员。
4.2 安装 Java 运行时
若第二章 2.1 节已完成可跳过本步,直接用 java -version 确认输出为 openjdk version "21.0.12.1" 即可继续。
4.3 创建运行用户与目录
Jelu 以专用系统用户运行,不可登录、只拥有自己的数据目录:
sudo useradd -r -d /var/lib/jelu -s /usr/sbin/nologin jelu
sudo mkdir -p /opt/jelu /var/lib/jelu
sudo chown jelu:jelu /opt/jelu /var/lib/jelu
验证用户与家目录:
id jelu
getent passwd jelu
实测回显:
uid=997(jelu) gid=997(jelu) groups=997(jelu)
jelu:x:997:997::/var/lib/jelu:/usr/sbin/nologin
说明:useradd -d /var/lib/jelu 指定的家目录有两个作用——Jelu 内部默认把数据库放在 ${user.home}/.jelu/database/ 下,且 systemd 单元的 WorkingDirectory 也将指向这里(4.7 节)。
4.4 下载 jar 并校验
cd /opt/jelu
sudo wget https://github.com/bayang/jelu/releases/download/v0.88.2/jelu-0.88.2.jar
⚠️ 网络说明(实测):见 2.3 节。直连限速不可用时改走 HTTP 代理(
curl -x http://代理主机:端口 -Lo jelu-0.88.2.jar https://github.com/bayang/jelu/releases/download/v0.88.2/jelu-0.88.2.jar),本文实测环境即经代理完成下载。
下载完成后做大小与 sha256 双重校验:
sudo ls -l jelu-0.88.2.jar
sudo sha256sum jelu-0.88.2.jar
实测回显:
-rw-r--r-- 1 root root 108216402 Oct 28 08:11 jelu-0.88.2.jar
1d063355fbde5e75dcc67f1a700cc113aa3f3f00b80e69b299c9cd682d4cb085 jelu-0.88.2.jar
sha256 与发布页摘要一致、字节数与发布页一致,方可进入下一步。
4.5 首次前台试跑(Liquibase 自动建库实录)
正式托管前先前台跑一次,确认应用能起来、数据库能初始化:
cd /var/lib/jelu
sudo -u jelu java -jar /opt/jelu/jelu-0.88.2.jar
首次启动时 Jelu 通过 Liquibase 自动建库,实测日志(输出节选,首次建库):
2026-10-28T08:15:23.360Z INFO 3908 --- [main] liquibase.lockservice : Successfully acquired change log lock
2026-10-28T08:15:23.586Z INFO 3908 --- [main] liquibase.ui : Liquibase: Update has been successful. Rows affected: 56
2026-10-28T08:15:23.588Z INFO 3908 --- [main] liquibase.lockservice : Successfully released change log lock
Rows affected: 56 表示 56 个变更集全部执行成功。随后启动完成的日志(输出节选,同环境已建库后的启动):
2026-10-28T09:18:13.364Z INFO 5204 --- [main] i.g.b.jelu.config.DatabasePoolConfig : Database pool size of 1 with journal mode DELETE
2026-10-28T09:18:17.359Z INFO 5204 --- [main] o.s.boot.tomcat.TomcatWebServer : Tomcat started on port 11111 (http) with context path '/'
2026-10-28T09:18:17.557Z INFO 5204 --- [main] i.github.bayang.jelu.JeluApplicationKt : Started JeluApplicationKt in 6.661 seconds (process running for 7.159)
此刻默认库已落在 ${user.home}/.jelu/database/jelu.db(本环境即 /var/lib/jelu/.jelu/database/jelu.db),日志在 ~/.jelu/database/jelu.log。确认能启动后按 Ctrl-C 停止,进入下一步把数据路径整理到规范位置。
4.6 写入外置配置文件
Jelu 内置一份默认配置(jar 内 BOOT-INF/classes/application.yml);Spring Boot 约定进程工作目录下的 application.yml 会自动覆盖内置默认值。利用这一点,在工作目录放一份外置配置,把数据库、图片、导入目录统一收拢到 /var/lib/jelu/:
sudo tee /var/lib/jelu/application.yml > /dev/null <<'EOF'
jelu:
database:
path: /var/lib/jelu/
files:
images: /var/lib/jelu/files/images/
imports: /var/lib/jelu/files/imports/
metadata:
calibre:
path: /usr/bin/fetch-ebook-metadata
cors:
allowed-origins:
- "*"
logging:
level:
org.springframework.security: DEBUG
EOF
sudo chown jelu:jelu /var/lib/jelu/application.yml
逐项说明:
jelu.database.path: /var/lib/jelu/:数据库文件、日志文件的根路径,覆盖后库文件为/var/lib/jelu/jelu.db、日志为/var/lib/jelu/jelu.log(不再藏在.jelu子目录);jelu.files.images/jelu.files.imports:封面与作者图片落盘目录、CSV/ISBN 导入文件目录;jelu.metadata.calibre.path: /usr/bin/fetch-ebook-metadata:元数据抓取工具路径(2.4 节安装 calibre 后的实际位置);未装 calibre 时删除该组配置并依赖插件提供方;jelu.cors.allowed-origins: "*":默认放行所有来源,若只经同域反代访问可收紧为具体域名;logging.level.org.springframework.security: DEBUG:安全认证链路调试日志,排障期开启,稳定后可改为INFO。
验证外置配置生效:重启试跑后日志应出现 started by jelu in /var/lib/jelu,且数据目录直下出现库文件。本环境实测:
sudo ls -l /var/lib/jelu/jelu.db
-rw-r--r-- 1 jelu jelu 278528 Oct 28 09:24 jelu.db
4.7 编写 systemd 单元
创建 /etc/systemd/system/jelu.service(内容与实测环境一致):
sudo tee /etc/systemd/system/jelu.service > /dev/null <<'EOF'
[Unit]
Description=Jelu self-hosted reading tracker
After=network.target
[Service]
Type=simple
User=jelu
Group=jelu
WorkingDirectory=/var/lib/jelu
ExecStart=/usr/bin/java -Xms256m -Xmx512m -Djava.io.tmpdir=/var/lib/jelu/tmp -jar /opt/jelu/jelu-0.88.2.jar
Restart=on-failure
RestartSec=5
[Install]
WantedBy=multi-user.target
EOF
逐项说明:
User/Group=jelu:以 4.3 节的专用低权用户运行,不使用 root;WorkingDirectory=/var/lib/jelu:必须是 jelu 用户可写的专属目录。Jelu 会在工作目录下生成 Lucene 全文搜索索引(write.lock、segments_2、_0.cfe等文件),工作目录不可写或残留锁文件会导致启动失败(排障实录见 9.2 节);ExecStart的 JVM 参数:-Xms256m -Xmx512m:堆内存限定在 256–512MB 区间,个人/家庭自托管足够;-Djava.io.tmpdir=/var/lib/jelu/tmp:把 Java 临时目录指到数据目录下。这一项很关键——SQLite JDBC 驱动启动时会把原生库.so解压到java.io.tmpdir(默认/tmp)再加载,若服务器/tmp以noexec挂载(安全加固常态)会加载失败、整个应用起不来(排障实录见 9.1 节);
Restart=on-failure+RestartSec=5:异常退出 5 秒后自动拉起。
创建临时目录并赋权:
sudo mkdir -p /var/lib/jelu/tmp
sudo chown jelu:jelu /var/lib/jelu/tmp
4.8 启动与开机自启
sudo systemctl daemon-reload
sudo systemctl enable jelu
sudo systemctl start jelu
三条命令各自执行:daemon-reload 让 systemd 读取新单元文件;enable 注册开机自启;start 立即启动。
4.9 验证服务
逐条执行并核对回显:
systemctl is-active jelu
active
systemctl status jelu --no-pager -l
实测回显(输出节选):
● jelu.service - Jelu self-hosted reading tracker
Loaded: loaded (/etc/systemd/system/jelu.service; enabled; preset: enabled)
Active: active (running) since Wed 2026-10-28 09:19:36 UTC; 1h 18min ago
Main PID: 5300 (java)
Tasks: 43 (limit: 2932)
Memory: 477.2M (peak: 478.2M)
CPU: 22.628s
CGroup: /system.slice/jelu.service
└─5300 /usr/bin/java -Xms256m -Xmx512m -Djava.io.tmpdir=/var/lib/jelu/tmp -jar /opt/jelu/jelu-0.88.2.jar
Memory: 477.2M 即 512MB 堆下的真实常驻内存,CGroup 行可直接核对 JVM 参数已生效。再用进程表复核一遍 JVM 参数(等效 jps -v 的验证手段):
ps -ef | grep '[j]ava'
jelu 5300 1 1 09:19 ? 00:00:22 /usr/bin/java -Xms256m -Xmx512m -Djava.io.tmpdir=/var/lib/jelu/tmp -jar /opt/jelu/jelu-0.88.2.jar
端口与 HTTP 接口验证:
ss -tlnp | grep 11111
LISTEN 0 100 *:11111 *:* users:(("java",pid=5300,fd=11))
curl -s http://127.0.0.1:11111/api/v1/server-settings
{"metadataFetchEnabled":true,"metadataFetchCalibreEnabled":true,"appVersion":"0.88.2","ldapEnabled":false,"metadataPlugins":[{"name":"calibre","order":1000}]}
appVersion: "0.88.2" 与所装 jar 版本一致,metadataPlugins 里出现 calibre 说明 4.6 节的元数据配置已加载。
4.10 创建管理员账户
服务就绪后,浏览器访问 http://<服务器IP>:11111 进入初始化页面,第一个注册的用户自动成为管理员(官方文档 First login 说明)。命令行方式同样可行(实测流程):先取会话 Cookie 与 CSRF 令牌,再提交创建请求:
# 1. 取会话与 CSRF 令牌(token 同时出现在 Cookie 与响应体中)
curl -s -c /tmp/cj http://127.0.0.1:11111/api/v1/token
{"token":"1fdea51c-f746-4d25-99dd-c91ed6c6a746"}
# 2. 携带 Cookie 与 X-XSRF-TOKEN 头创建首个管理员(示例演示账号 admin/q1w2e3r4)
TOK=$(python3 -c "import json;print(json.load(open('/dev/stdin')).get('token',''))" < <(curl -s -c /tmp/cj2 http://127.0.0.1:11111/api/v1/token))
curl -s -w '\n=> HTTP %{http_code}\n' -b /tmp/cj2 -X POST http://127.0.0.1:11111/api/v1/users \
-H 'Content-Type: application/json' -H "X-XSRF-TOKEN: $TOK" \
-d '{"login":"admin","password":"q1w2e3r4","email":"","isAdmin":true}'
实测回显(响应体同形、HTTP 200;id 为平台生成的 UUID,每次不同):
{"id":"8ab5893d-6f33-3ebd-0a80-539927125ff7","creationDate":"2026-10-28T09:16:35Z","login":"admin","password":"****","modificationDate":"2026-10-28T09:16:35Z","admin":true,"provider":"JELU_DB"}
=> HTTP 200
创建后初始化状态随之关闭(isInitialSetup 由 true 变 false,实测回显):
curl -s http://127.0.0.1:11111/api/v1/setup/status
{"isInitialSetup":false}
用 Basic 认证验证管理员可登录:
curl -s -u admin:q1w2e3r4 http://127.0.0.1:11111/api/v1/users/me
{"user":{"id":"8ab5893d-6f33-3ebd-0a80-539927125ff7","creationDate":null,"login":"admin","password":"****","modificationDate":null,"admin":true,"provider":"JELU_DB"},"token":"9b93e72a-b757-4654-92f8-335410448463"}
至此浏览器以 admin / q1w2e3r4 登录即可开始录入书目。
4.11 防火墙放行(按需)
直连 11111 端口访问时放行;若只走 7.3 节的 nginx 反代(80/443),则只放行反代端口、不对公网暴露 11111。以 ufw 为例(⚠️ 需要已安装并启用 ufw 的真机环境;本文实测环境未启用 ufw,命令为标准用法):
sudo ufw allow 11111/tcp comment 'Jelu'
五、方案 B:离线安装(断网内网)
5.1 方案说明
目标服务器完全断网时,用一台同版本同架构(Ubuntu 24.04 amd64)的在线机准备全部物料(jar、Java 及其依赖的 deb 包),经移动介质拷入内网安装。Jelu 本体只是「一个 jar + 一个 systemd 单元」,离线安装没有任何额外组件。
5.2 在线机准备物料
物料一:jar 与校验值(同 4.4 节,sha256sum 输出必须一致)。
物料二:Java 运行时及其全部依赖 deb 包。用 --download-only 让 apt 把 openjdk-21-jre-headless 连同依赖一起下载到本地缓存目录(不安装):
sudo apt-get install --download-only -y openjdk-21-jre-headless
ls /var/cache/apt/archives/*.deb
若只需单独验证主包,apt-get download 的实测回显如下:
Get:1 http://archive.ubuntu.com/ubuntu noble-updates/main amd64 openjdk-21-jre-headless amd64 21.0.12.1+1-1~24.04.4 [46.5 MB]
Fetched 46.5 MB in 3s (13.7 MB/s)
-rw-r--r-- 1 root root 46499662 Sep 21 16:30 openjdk-21-jre-headless_21.0.12.1+1-1~24.04.4_amd64.deb
把 /var/cache/apt/archives/ 下的全部 deb 与 jar 一起打包拷贝:
tar czf jelu-offline.tar.gz -C /var/cache/apt/archives . -C / jelu-offline-jar/jelu-0.88.2.jar
💡 技术拓展:离线装 deb 的两种常见方式——
dpkg -i *.deb(把依赖包与主包放同一目录一次性安装,适合完全断网)、apt-get install -f(联网修复缺失依赖,断网不可用)。内网多台部署时也可自建 apt 源(dpkg-dev的dpkg-scanpackages),本文按单机dpkg -i演示。
5.3 内网机安装 Java
sudo dpkg -i /path/to/debs/*.deb
java -version
全部 deb 在同一目录一次安装时,依赖关系可一次满足;java -version 输出与 2.1 节一致即成功。
5.4 内网机部署 jar 与 systemd
jar 拷贝到 /opt/jelu/jelu-0.88.2.jar 后,完全复用 4.3–4.8 节的命令与文件:创建运行用户与目录(4.3)、写入 /var/lib/jelu/application.yml(4.6,原样照抄)、创建临时目录(4.7)、写入 /etc/systemd/system/jelu.service(4.7,原样照抄),然后:
sudo systemctl daemon-reload
sudo systemctl enable jelu
sudo systemctl start jelu
5.5 验证
与 4.9 节完全一致:systemctl is-active jelu 返回 active,curl -s http://127.0.0.1:11111/api/v1/server-settings 返回含 appVersion 的 JSON,即离线安装完成。唯一差异:断网环境下 fetch-ebook-metadata 无法在线抓取元数据,录入书目只能手动填写(联网后再补抓)。
六、方案对比与选择建议
| 维度 | 方案 A:在线安装(官方 jar + systemd) | 方案 B:离线安装 |
|---|---|---|
| 是否需联网 | 需要(GitHub Releases 下载 jar) | 仅在线机需要,目标机全程断网 |
| Java 获取 | apt 官方源直接装 | --download-only 打包 deb 拷入后 dpkg -i |
| jar 获取 | wget/curl 直下或经代理 | 外网机下载后 sha256 校验再拷入 |
| 版本更新 | 直接下新 jar 替换 | 每次升级都要重新走物料打包 |
| 适用场景 | 能出网的云主机/家用服务器 | 内网隔离的家庭/企业 NAS |
选择建议:目标机可出网选方案 A(本文主线,步骤最短);内网隔离环境选方案 B,且把 jar 的 sha256 校验当作强制步骤,防止介质传递过程中文件损坏。
七、配置说明
7.1 配置文件与加载机制
Jelu 的配置有三层覆盖关系:jar 内置默认值 → 工作目录 application.yml 覆盖 → 命令行 --参数 覆盖。本文 4.6 节用的是第二层(推荐:随 systemd 单元的 WorkingDirectory 生效,改完 systemctl restart jelu 即生效)。查看内置默认值的命令:
unzip -p /opt/jelu/jelu-0.88.2.jar BOOT-INF/classes/application.yml
全部参数(数据库 journalMode、会话时长、日志滚动、认证方式、元数据提供方等)的逐项实测详解见《Jelu 0.88.2 配置参数详解》。
7.2 JVM 内存调整
个人/家庭自托管场景把堆限定在 256–512MB 即可。调整位置就是 4.7 节单元文件里的 ExecStart 行:把 -Xms256m -Xmx512m 改为目标值(如 -Xms256m -Xmx256m 进一步压缩到 256MB 堆),随后重载并重启:
sudo systemctl daemon-reload
sudo systemctl restart jelu
ps -ef | grep '[j]ava'
ps -ef 输出中出现新的 -Xms… -Xmx… 取值即生效(本文实测生效回显见 4.9 节);也可用 systemctl status jelu 的 Memory: 行观察常驻内存变化。
7.3 nginx 反向代理(可选)
Jelu 的 Web UI 与 API 同端口,反代只需转发一个 upstream。创建 /etc/nginx/sites-available/jelu.conf:
server {
listen 80;
server_name _;
location / {
proxy_pass http://127.0.0.1:11111;
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;
}
}
启用并重载(本文实测环境即此配置,监听 80 转发 11111):
sudo ln -s /etc/nginx/sites-available/jelu.conf /etc/nginx/sites-enabled/jelu.conf
sudo nginx -t
sudo systemctl reload nginx
nginx -t 通过后 reload 生效,浏览器走 80 端口访问即可。若后续配置 HTTPS,把 proxy_set_header X-Forwarded-Proto $scheme; 中的 $scheme 固定为 https,否则 Jelu 生成的绝对链接会退回 http。
7.4 数据备份要点
Jelu 全部业务数据在 SQLite 单文件 jelu.db(外加封面图片目录 files/images/)。最稳妥的备份是停服后整目录拷贝:
sudo systemctl stop jelu
sudo tar czf /var/backup/jelu-$(date +%F).tar.gz -C /var/lib/jelu jelu.db application.yml files
sudo systemctl start jelu
若数据库启用 wal 模式(见《Jelu 0.88.2 配置参数详解》),热备份必须连 jelu.db-wal、jelu.db-shm 一起拷贝或先停服;本文默认 delete 模式下 jelu.db 单文件即可。
八、升级与回滚
8.1 升级步骤(多版本并存)
Jelu 升级就是「换 jar + 重启」,数据库表结构由 Liquibase 在启动时自动迁移(4.5 节实测的 Rows affected: 56 即该机制):
- 备份数据(7.4 节命令);
- 下载新版本 jar 到
/opt/jelu/(如jelu-0.89.0.jar),与旧版并存、sha256sum校验; - 修改
/etc/systemd/system/jelu.service的ExecStart行,把 jar 文件名指到新版本; sudo systemctl daemon-reload后sudo systemctl restart jelu;- 验证:
curl -s http://127.0.0.1:11111/api/v1/server-settings中的appVersion已变为新版本,日志中出现 Liquibase 增量迁移(有表结构变更时)与Started JeluApplicationKt; - 观察一两天稳定后,删除旧 jar 释放 103MB 空间。
8.2 回滚
新版本启动异常时回滚:停服 → 恢复升级前的 jelu.db 备份 → 把 ExecStart 行改回旧 jar 文件名 → daemon-reload + start。
⚠️ 注意:Liquibase 迁移只向前兼容——新版本对库做过表结构变更后,直接换回旧 jar 可能因表结构不匹配而启动失败,所以回滚必须连同 8.1 步骤 1 的数据库备份一起恢复,这也是「升级前必备份」的原因。
九、常见问题(FAQ)
Q1:启动失败,日志报 Failed to load native library: sqlite-…-libsqlitejdbc.so 与 failed to map segment from shared object,随后 Application run failed
这是本文实测遇到的第一个真实故障。完整排障过程:
- 现象(
/var/lib/jelu/jelu.log实测节选):
2026-10-28T08:14:36.152Z ERROR 3861 --- [main] org.sqlite.SQLiteJDBCLoader : Failed to load native library: sqlite-3.53.2.0-778b9383-b279-4f74-918f-08d0243fd87a-libsqlitejdbc.so. osinfo: Linux/x86_64
java.lang.UnsatisfiedLinkError: /tmp/sqlite-3.53.2.0-778b9383-b279-4f74-918f-08d0243fd87a-libsqlitejdbc.so: /tmp/sqlite-3.53.2.0-778b9383-b279-4f74-918f-08d0243fd87a-libsqlitejdbc.so: failed to map segment from shared object
- 定位:SQLite JDBC 驱动(sqlite-jdbc)启动时把原生库
.so解压到java.io.tmpdir(默认/tmp)再dlopen加载;failed to map segment是典型的 noexec 限制。实测确认挂载参数:
findmnt -no OPTIONS /tmp
rw,nosuid,nodev,noexec,relatime,inode64
- 解决:把 Java 临时目录指到可执行的数据目录(4.7 节单元文件中的
-Djava.io.tmpdir=/var/lib/jelu/tmp),重启后日志出现Database pool size of 1 with journal mode DELETE且Started JeluApplicationKt正常,故障消除。
Q2:启动失败,报 NoSuchFileException: <工作目录>/write.lock 或 AccessDeniedException: <工作目录>/write.lock
Jelu 用 Lucene 维护书名/作者的全文搜索索引,索引文件(write.lock、segments_2、_0.cfe 等)直接生成在进程工作目录下。本文实测在把 WorkingDirectory 指到 jar 同目录(/opt/jelu)时触发过该异常(异常链实测节选:Caused by: java.nio.file.NoSuchFileException: /opt/jelu/write.lock,并伴随 Suppressed: java.nio.file.AccessDeniedException: /opt/jelu/write.lock)。处置:
- systemd 单元的
WorkingDirectory必须指向 jelu 用户可写的专属目录(本文采用/var/lib/jelu,见 4.7 节); - 若是上一次异常退出残留的锁文件,确认无残留 java 进程(
ps -ef | grep '[j]ava')后删除工作目录下的write.lock再启动。
本文实测:把工作目录调整为 /var/lib/jelu 并清理残留后,连续重启多次未再复现。
Q3:每次启动日志都有 ERROR「The use of configuration keys that are no longer supported … spring.mvc.throw-exception-if-no-handler-found」
0.88.2 内置默认配置里的 spring.mvc.throw-exception-if-no-handler-found 键在 Spring Boot 4 运行时下已废弃,每次启动都会打印这段 PropertiesMigrationListener 报告(实测回显节选):
The use of configuration keys that are no longer supported was found in the environment:
Property source 'Config resource 'class path resource [application.yml]' via location 'optional:classpath:/'':
Key: spring.mvc.throw-exception-if-no-handler-found
Line: 25
Reason: DispatcherServlet property is deprecated for removal and should no longer need to be configured.
实测结论:成功启动的实例日志中该 ERROR 持续出现但服务运行正常(本文实测实例连续运行处理请求无异常),属于上游内置配置与新运行时的兼容性提示;只有当它与其它启动期异常叠加时才可能中断启动,此时按完整日志先解决其它根因。
Q4:Web server failed to start. Port 11111 was already in use.
端口被占。先找占用者:ss -tlnp | grep 11111;若需要临时拉起第二个实例做验证,可换端口与独立库路径前台启动(实测回显):
java -Djava.io.tmpdir=/var/lib/jelu/tmp -jar /opt/jelu/jelu-0.88.2.jar --server.port=11112 --jelu.database.path=/tmp/jelu-p12/
2026-10-28T11:01:24.674Z INFO 5960 --- [main] o.s.boot.tomcat.TomcatWebServer : Tomcat started on port 11112 (http) with context path '/'
正式改端口则修改配置文件中的 server.port(重启生效)。⚠️ 生产环境不要长期用 /tmp 做数据路径,此处仅为端口验证演示。
Q5:中文 ISBN 在线抓取不到元数据
Jelu 默认经 calibre 的 fetch-ebook-metadata 抓取,其检索源以 Google Books、Amazon 为主,对国内出版社的 ISBN 覆盖差。实测(ISBN 9787532776413,输出节选):
Using plugins: Google (1, 1, 1), Amazon.com (1, 3, 7)
****************************** Google (1, 1, 1) ******************************
Found 0 results
****************************** Amazon.com (1, 3, 7) ******************************
Found 0 results
处置:改用「题名 + 作者」搜索(匹配英文书名时命中率高);或按官方配置文档部署 google/inventaire.io 等插件提供方补充源;都失败时可手动填写书目信息录入,不影响后续追踪功能。
十、卸载与清理
sudo systemctl disable --now jelu
sudo rm /etc/systemd/system/jelu.service
sudo systemctl daemon-reload
sudo rm -rf /opt/jelu /var/lib/jelu
sudo userdel jelu
逐条说明:disable --now 停止服务并取消开机自启;删除单元文件后重载 systemd;删除 jar 与全部数据(含数据库,删除前确认备份已另存);删除运行用户。配置过 7.3 节反代的再删除站点文件并重载 nginx(每行一个单元):
sudo rm /etc/nginx/sites-enabled/jelu.conf
sudo systemctl reload nginx
验证清理结果:systemctl status jelu 提示单元不存在、ls /opt/jelu /var/lib/jelu 提示 No such file or directory、getent passwd jelu 无输出。
十一、参考链接
- 官方仓库(README/Releases,查询日期 2026-10-28):https://github.com/bayang/jelu
- v0.88.2 发布页(jar 资产与 sha256 摘要):https://github.com/bayang/jelu/releases/tag/v0.88.2
- 官方文档站:https://bayang.github.io/jelu-web/
- 官方 jar 安装文档(Java 版本要求):https://bayang.github.io/jelu-web/installation/jar/
- 官方配置文档(元数据提供方/认证/CORS):https://bayang.github.io/jelu-web/configuration/
- 内置默认配置(main 分支 application.yml):https://raw.githubusercontent.com/bayang/jelu/main/src/main/resources/application.yml
- 站内延伸:《Jelu 介绍与选型指南》、《Jelu 0.88.2 配置参数详解》、《Jelu 0.88.2 常用 CLI 命令与工具详解》