Ubuntu 22.04 安装 Halo 2.27.1 指南
Ubuntu 22.04 裸机安装 Halo 2.27.1 全流程:Temurin JDK 21 + halo.jar + systemd 托管,H2 快速起步与外接 PostgreSQL 两套方案,含初始化、验证回显、JVM 内存限定、常见问题与卸载。
Ubuntu 22.04 安装 Halo 2.27.1 指南
目标:在一台 Ubuntu 22.04 服务器上,从零安装 Halo 2.27.1(社区版),用 Temurin JDK 21 运行官方 JAR,systemd 托管开机自启,先用 H2 快速跑通并完成站点初始化,再切换到 PostgreSQL 验证生产可用。全文命令与回显于 2026-10-27 在 Ubuntu 22.04.5 LTS 实测。
一、环境信息
- 操作系统:Ubuntu 22.04.5 LTS(x86_64)
- 内核与资源:2 vCPU / 4 GB 内存 / 40 GB 磁盘(实测机型)
- JDK:Eclipse Temurin 21.0.13+9 LTS(
/usr/local/jdk-21.0.13,软链/usr/local/java) - Halo:2.27.1(
halo-2.27.1.jar,134,790,347 字节) - 数据库方案一:H2 文件库(
/root/.halo2/db/halo-next.mv.db) - 数据库方案二:PostgreSQL 14.24(Ubuntu 22.04 官方源)
- 服务端口:8090
- 工作目录:
/root/.halo2 - 站点地址:
http://127.0.0.1:8090 - 管理账号(示例):
root/q1w2e3r4
二、前置条件
安装前确认三件事:系统版本、内存、端口占用。
cat /etc/os-release | head -2
free -h | head -2
sudo ss -lntp | grep 8090 || echo "8090 未被占用"
实测回显:
PRETTY_NAME="Ubuntu 22.04.5 LTS"
NAME="Ubuntu"
total used free shared buff/cache available
Mem: 3.8Gi 265Mi 3.4Gi 1.0Mi 157Mi 3.4Gi
8090 未被占用
Halo 2.27.1 只依赖 Java 21 运行环境和一个数据库。单站最低建议 2 GB 内存;若同时跑 PostgreSQL,建议 4 GB 以上。磁盘按内容量预估,附件多时另配对象存储更划算。
三、版本选型说明
| 版本 | 说明 | 建议 |
|---|---|---|
| 2.27.1(本文) | 2026-10-23 发布,含安全修复(HTTP/1.1 keep-alive、eMudhra 证书等),Spring Boot 升级至 4.1.2 | 新装首选 |
| 2.27.0 | 2026-10-14 发布的功能版本 | 可按需,但建议直接上补丁版 |
| 2.26.x | 上一功能线 | 仅在插件兼容性受限时考虑 |
| 1.x | 已停止主线维护,插件/主题体系与 2.x 不兼容 | 应尽快迁移 |
⚠️ 注意:Halo 2.27.1 的字节码目标是 Java 21(源码
options.release = 21),不是不少文章里写的 Java 17。用 Java 17 启动会直接报UnsupportedClassVersionError,安装时必须准备 21。
四、方案 A:官方 JAR + systemd(H2 快速起步)
这条路线最短:装 JDK、放 JAR、写配置文件、交给 systemd,10 分钟内可访问初始化页。
4.1 安装 Temurin JDK 21
从 Adoptium 官方接口下载最新 21 GA 包,解压到 /usr/local:
sudo mkdir -p /usr/local /opt/halo
curl -L -o /tmp/jdk21.tar.gz \
"https://api.adoptium.net/v3/binary/latest/21/ga/linux/x64/jdk/hotspot/normal/eclipse"
sudo tar -xzf /tmp/jdk21.tar.gz -C /usr/local/
sudo mv /usr/local/jdk-21.0.13+9 /usr/local/jdk-21.0.13
sudo ln -sfn /usr/local/jdk-21.0.13 /usr/local/java
/usr/local/java/bin/java -version
实测回显:
openjdk version "21.0.13" 2026-10-20 LTS
OpenJDK Runtime Environment Temurin-21.0.13+9 (build 21.0.13+9-LTS)
OpenJDK 64-Bit Server VM Temurin-21.0.13+9 (build 21.0.13+9-LTS, mixed mode, sharing)
⚠️ 网络说明:实测时
api.adoptium.net会 307 跳转到 GitHub 资产,官方直连返回 000(超时),改用代理192.168.1.62:7897后下载成功(200,约 199 MB)。国内服务器也可改用清华/阿里云软件源提供的 Temurin 安装包,或apt install openjdk-21-jdk(需先添加相应软件源)。
如果希望用系统包管理器而不是 tar 包,Ubuntu 22.04 官方源没有 21,需要添加第三方源:
sudo apt update
sudo apt install -y openjdk-17-jdk-headless # 仅示意:22.04 源里最高是 17,不能用于 Halo 2.27
java -version
实测该命令得到的是 17,无法启动 Halo 2.27.1,因此本文继续使用 Temurin tar 包。
4.2 下载 Halo JAR
sudo curl -L -o /opt/halo/halo-2.27.1.jar \
https://github.com/halo-dev/halo/releases/download/v2.27.1/halo-2.27.1.jar
sudo ln -sfn /opt/halo/halo-2.27.1.jar /opt/halo/halo.jar
ls -lh /opt/halo/
实测回显:
total 131632
-rw-r--r-- 1 root root 134790347 Oct 27 12:55 halo-2.27.1.jar
lrwxrwxrwx 1 root root 25 Oct 27 12:58 halo.jar -> /opt/halo/halo-2.27.1.jar
⚠️ 网络说明:GitHub Release 直连实测不可用(HTTP 000),改走代理
192.168.1.62:7897后 200,下载 134,790,347 字节。官方同时提供.sha256文件,生产环境建议下载后校验。
⚠️ 注意:官方资产名是
halo-2.27.1.jar,而多数教程的 systemd 单元里写的是halo.jar。两者必须一致——首次实测因只放了带版本号的文件、单元里却指向halo.jar,服务进入无限重启:Error: Unable to access jarfile /opt/halo/halo.jar halo.service: Main process exited, code=exited, status=1/FAILURE补一条软链
halo.jar -> halo-2.27.1.jar即可,后续升级只改软链指向,systemd 单元不用动。
4.3 编写 application.yaml
sudo mkdir -p /root/.halo2
sudo tee /root/.halo2/application.yaml > /dev/null <<'EOF'
server:
port: 8090
spring:
r2dbc:
url: r2dbc:h2:file:///${halo.work-dir}/db/halo-next?MODE=MySQL&DB_CLOSE_ON_EXIT=FALSE
username: admin
password: q1w2e3r4
sql:
init:
mode: always
platform: h2
halo:
work-dir: /root/.halo2
external-url: http://127.0.0.1:8090
EOF
逐项说明:
server.port:监听端口,默认 8090;改端口后halo.external-url要同步。spring.r2dbc.url:H2 文件库地址,${halo.work-dir}由下面的halo.work-dir解析;MODE=MySQL让 H2 以 MySQL 兼容模式运行。spring.r2dbc.username/password:H2 的连接凭据,只在首次建库时生效,后续修改不会改变已有库的密码。spring.sql.init.mode=always:每次启动执行数据库初始化脚本(Halo 自带迁移器会做幂等处理)。spring.sql.init.platform=h2:声明数据库方言;换 PostgreSQL 时必须改成postgresql。halo.work-dir:工作目录,存放db/、attachments/、themes/、plugins/、keys/、logs/。halo.external-url:站点对外地址,影响初始化页预填、邮件链接与永久链接。
💡 说明:配置文件也可以不写,直接用命令行参数覆盖(
--server.port=8090等)。JAR 部署推荐配置文件方式,升级时只替换 JAR、配置不动。
4.4 systemd 托管
sudo tee /etc/systemd/system/halo.service > /dev/null <<'EOF'
[Unit]
Description=Halo
After=network.target
[Service]
Type=simple
WorkingDirectory=/opt/halo
Environment="JAVA_HOME=/usr/local/java"
Environment="JAVA_OPTS=-Xms256m -Xmx512m"
ExecStart=/usr/local/java/bin/java -Dfile.encoding=UTF-8 $JAVA_OPTS -jar /opt/halo/halo.jar --spring.config.additional-location=optional:file:/root/.halo2/
Restart=on-failure
RestartSec=5
[Install]
WantedBy=multi-user.target
EOF
sudo systemctl daemon-reload
sudo systemctl enable halo
sudo systemctl start halo
参数说明:
WorkingDirectory:进程工作目录,JAR 与相对路径以此为基准。Environment=JAVA_OPTS:把堆限定在 256–512 MB,避免 JVM 按物理内存 1/4 取上限。--spring.config.additional-location=optional:file:/root/.halo2/:告诉 Spring Boot 额外读取工作目录下的application.yaml,末尾斜杠不能省。Restart=on-failure+RestartSec=5:异常退出 5 秒后重试,避免配置错误时高频重启刷日志。
4.5 启动与验证
sudo systemctl status halo --no-pager | head -12
实测回显:
● halo.service - Halo
Loaded: loaded (/etc/systemd/system/halo.service; enabled; vendor preset: enabled)
Active: active (running) since Tue 2026-10-27 12:58:27 UTC; 5min ago
Main PID: 197 (java)
Tasks: 53 (limit: 2932)
Memory: 643.7M
CPU: 34.415s
└─197 /usr/local/java/bin/java -Dfile.encoding=UTF-8 -Xms256m -Xmx512m -jar /opt/halo/halo.jar --spring.config.additional-location=optional:file:/root/.halo2/
启动日志(节选):
12:58:28,682 |-WARN in c.q.l.core.rolling.SizeAndTimeBasedRollingPolicy@1650955365 - 'maxHistory' is not set, ignoring 'totalSizeCap' option with value [1 GB]
__ __ __
/ / / /___ _/ /___
/ /_/ / __ `/ / __ \
/ __ / /_/ / / /_/ /
/_/ /_/\__,_/_/\____/
Version: 2.27.1
2026-10-27T12:58:28.776Z INFO 197 --- [ main] run.halo.app.Application : Starting Application v2.27.1 using Java 21.0.13 with PID 197 (/opt/halo/halo-2.27.1.jar started by root in /opt/halo)
2026-10-27T12:58:31.568Z INFO 197 --- [ main] org.pf4j.DefaultPluginManager : PF4J version 3.16.0 in 'deployment' mode
2026-10-27T12:58:32.729Z INFO 197 --- [ main] r.h.a.s.a.impl.RsaKeyService : Wrote RSA keys for PAT into /root/.halo2/keys/pat_id_rsa and /root/.halo2/keys/pat_id_rsa.pub
2026-10-27T12:58:33.518Z INFO 197 --- [ main] r.h.a.search.lucene.LuceneSearchEngine : Initialized lucene search engine
2026-10-27T12:58:35.458Z INFO 197 --- [ main] o.s.boot.reactor.netty.NettyWebServer : Netty started on port 8090 (http)
2026-10-27T12:58:35.544Z INFO 197 --- [ main] run.halo.app.Application : Started Application in 7.196 seconds (process running for 7.677)
关键信息:版本 2.27.1、Java 21.0.13、PF4J 3.16.0 插件框架、Lucene 搜索引擎已初始化、首次启动自动生成 PAT 用的 RSA 密钥、7.2 秒完成启动。
端口监听与首页访问:
sudo ss -lntp | grep 8090
curl -s -o /dev/null -w "root=%{http_code}\n" http://127.0.0.1:8090/
curl -s -o /dev/null -w "console=%{http_code}\n" http://127.0.0.1:8090/console
实测回显:
LISTEN 0 4096 *:8090 *:* users:(("java",pid=3200,fd=44))
root=404
console=302
首页 404 是未初始化、未启用主题时的正常表现;/console 302 会跳转到登录/初始化页。
4.6 首次初始化
在浏览器打开 http://<服务器IP>:8090/console,会进入 Setup 页面,填写:
- 站点标题:
Halo 技术文档站 - 管理员用户名:
root - 邮箱:
root@quzuo.net - 密码:
q1w2e3r4 - 外部访问地址:
http://127.0.0.1:8090(或你的域名) - 语言:简体中文
提交后页面返回 204,站点立即可访问:
curl -s http://127.0.0.1:8090/ | grep -o "<title>[^<]*</title>"
curl -s -o /dev/null -w "home=%{http_code}\n" http://127.0.0.1:8090/
实测回显:
<title>Halo 技术文档站</title>
home=200
初始化完成后,Halo 会自动安装并启动默认主题与两个内置插件。日志中可见:
r.halo.app.plugin.DefaultSpringPlugin : After publishing plugin started event for plugin ai-page-builder
r.h.a.core.reconciler.PluginReconciler : Plugin ai-page-builder started with state STARTED.
⚠️ 注意:初始化表单的
language只接受zh-CN、zh-TW、en、es。传zh会得到 400:{"detail":"Validation failure","errors":["must match \"^(zh-CN|zh-TW|en|es)$\""],"status":400,"title":"Bad Request"}用脚本/curl 提交时必须带页面下发的
_csrf与对应 Cookie,否则同样被拒。
初始化后工作目录内容:
sudo du -sh /root/.halo2
sudo ls -la /root/.halo2
实测回显:
26M /root/.halo2
drwxr-xr-x 2 root root 4096 Oct 27 12:58 backups
drwxr-xr-x 2 root root 4096 Oct 27 12:58 db
drwxr-xr-x 3 root root 4096 Oct 27 12:58 indices
drwxr-xr-x 2 root root 4096 Oct 27 12:58 keys
drwxr-xr-x 2 root root 4096 Oct 27 12:58 logs
drwxr-xr-x 3 root root 4096 Oct 27 13:00 plugins
drwxr-xr-x 3 root root 4096 Oct 27 13:00 themes
4.7 JVM 内存限定与生效验证
Halo 自身不限制堆,必须显式加 -Xms/-Xmx。本文在 systemd unit 中通过 Environment="JAVA_OPTS=-Xms256m -Xmx512m" 注入,验证是否生效:
ps -ef | grep [j]ava
/usr/local/java/bin/jps -v
实测回显:
root 197 1 21 12:58 ? 00:00:34 /usr/local/java/bin/java -Dfile.encoding=UTF-8 -Xms256m -Xmx512m -jar /opt/halo/halo.jar --spring.config.additional-location=optional:file:/root/.halo2/
197 halo.jar -Dfile.encoding=UTF-8 -Xms256m -Xmx512m
💡 说明:
-Xms256m -Xmx512m指堆的初始/上限,不含元空间、直接内存与 Netty 堆外缓存。实测单站常驻 RSS 约 640 MB(见 systemd 的Memory: 643.7M),2 GB 内存机器仍可运行,但插件较多时建议把上限提到 512–1024 MB。
五、方案 B:外接 PostgreSQL(生产推荐)
H2 只适合验证功能。官方明确不推荐生产使用 H2(易因操作不当损坏数据文件),生产建议 PostgreSQL 或 MySQL。
5.1 安装 PostgreSQL
Ubuntu 22.04 官方源提供 PostgreSQL 14:
sudo apt update
sudo apt install -y postgresql postgresql-client
sudo systemctl start postgresql
sudo systemctl is-active postgresql
pg_lsclusters
psql --version
实测回显:
active
Ver Cluster Port Status Owner Data directory Log file
14 main 5432 online postgres /var/lib/postgresql/14/main /var/log/postgresql/postgresql-14-main.log
psql (PostgreSQL) 14.24 (Ubuntu 14.24-0ubuntu0.22.04.1)
5.2 建库建用户
Halo 不会自动创建数据库,必须先建库。使用专用账号 halo(示例密码 q1w2e3r4):
sudo -u postgres psql -c "CREATE USER halo WITH PASSWORD 'q1w2e3r4';"
sudo -u postgres psql -c "CREATE DATABASE halo OWNER halo ENCODING 'UTF8';"
sudo -u postgres psql -c "SELECT datname FROM pg_database WHERE datname='halo';"
实测回显:
CREATE ROLE
CREATE DATABASE
datname
---------
halo
💡 说明:字符集必须为 UTF8,否则中文内容会乱码。若使用 MySQL,需先
CREATE DATABASE halo CHARACTER SET utf8mb4 COLLATE utf8mb4_bin;。
5.3 修改 Halo 配置
把 application.yaml 的 spring 段替换为 PostgreSQL 连接(其余不变):
sudo tee /root/.halo2/application.yaml > /dev/null <<'EOF'
server:
port: 8090
spring:
r2dbc:
url: r2dbc:pool:postgresql://127.0.0.1:5432/halo
username: halo
password: q1w2e3r4
sql:
init:
mode: always
platform: postgresql
halo:
work-dir: /root/.halo2
external-url: http://127.0.0.1:8090
EOF
sudo systemctl restart halo
5.4 验证数据库确实切换
日志中的方言标识能确认连接的是 PostgreSQL:
journalctl -u halo --since "-2 min" --no-pager | grep -i "dialect"
实测回显(节选):
n.n.r2dbc.migrate.core.R2dbcMigrate : Configured with R2dbcMigrateProperties{..., dialect=POSTGRESQL, ..., migrationsTable='migrations', ...}
n.n.r2dbc.migrate.core.R2dbcMigrate : Successfully got result '42' of test query
Netty started on port 8090 (http)
Started Application in 10.367 seconds (process running for 11.37)
库内表结构(Halo 采用「扩展模型单表」设计,所有资源都落在 extensions):
sudo -u postgres psql -d halo -c "\dt"
sudo -u postgres psql -d halo -tAc "SELECT count(*) FROM extensions;"
实测回显:
List of relations
Schema | Name | Type | Owner
--------+------------+-------+-------
public | extensions | table | halo
public | migrations | table | halo
(2 rows)
196
切换到新库后站点处于未初始化状态,需要重新走一遍 Setup(同 4.6)。初始化完成后验证:
sudo systemctl restart halo
sleep 20
sudo systemctl is-active halo
curl -s -o /dev/null -w "home=%{http_code}\n" http://127.0.0.1:8090/
实测回显:
active
home=200
⚠️ 注意:换数据库不会迁移已有数据。H2 库里的文章、用户不会自动搬到 PostgreSQL,切换即等于全新站点;工作目录里的附件、主题、插件仍在,但内容需通过备份导入或重新初始化。生产上切换前务必导出备份。
⚠️ 注意:切换数据库那次重启,旧进程退出时日志出现插件卸载失败:
run.halo.app.extension.exception.SchemeNotFoundException: 500 INTERNAL_SERVER_ERROR "Scheme not found for aifoundation.halo.run/v1alpha1/AiProvider" ERROR ... org.pf4j.AbstractPluginManager : Cannot stop plugin 'ai-foundation@1.1.1'原因是新库中尚无对应扩展定义,停机时插件找不到模型。它不影响新实例启动,但升级/迁移前建议先在后台停用插件,再换库。
六、方案对比与选择建议
| 维度 | 方案 A:H2 | 方案 B:PostgreSQL |
|---|---|---|
| 依赖 | 无(随 JAR 内置) | 需安装并维护数据库服务 |
| 初始化速度 | 快,7 秒级启动 | 10 秒级启动 |
| 数据安全 | 单文件,易损坏,官方不推荐生产 | 事务与备份成熟 |
| 多实例/扩容 | 不支持(文件锁) | 支持多实例连接同一库 |
| 备份方式 | 停机拷 .mv.db | pg_dump 在线备份 |
| 适用场景 | 本地试用、功能验证、极小流量 | 生产站点、团队协作 |
选择建议:验证功能或临时演示用方案 A;只要站点对公网开放、有真实内容,直接用方案 B。
七、配置说明
7.1 端口与外部地址
改端口要同时改两处,否则邮件链接、站点地图会指向错误地址:
server:
port: 8091
halo:
external-url: https://blog.example.com
服务端只监听本机端口,公网访问还需放行防火墙(示例用 ufw):
sudo ufw allow 8090/tcp
sudo ufw status | grep 8090
实测回显:
8090/tcp ALLOW Anywhere
7.2 反向代理
生产环境建议由 Nginx 终止 TLS 并转发到 8090,注意 client_max_body_size 要放大,否则大附件上传失败:
server {
listen 80;
server_name blog.example.com;
client_max_body_size 1024m;
location / {
proxy_pass http://127.0.0.1:8090;
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;
}
}
改完执行 sudo nginx -t && sudo systemctl reload nginx,并将 halo.external-url 改为 https://blog.example.com 后重启 Halo。
八、常见问题
8.1 服务无限重启,日志只有 Unable to access jarfile
单元里的 JAR 路径与文件名不一致。核对:
ls -l /opt/halo/
sudo systemctl status halo --no-pager | head -6
解决:建立软链 ln -sfn /opt/halo/halo-2.27.1.jar /opt/halo/halo.jar,再 systemctl restart halo。
8.2 初始化提交报 400 Validation failure
language 传了 zh。改为 zh-CN;若用 curl,还要带 _csrf 与 Cookie。
8.3 首页 404、控制台 302
未初始化或未启用主题都属于此现象。完成 Setup 后首页即返回 200;若仍 404,检查后台「主题」是否已启用。
8.4 内存持续增长 / 被 OOM Killer 终止
先确认堆参数是否生效(jps -v),再限制进程或主机内存。Halo 的 Lucene 索引、Netty 堆外缓存会额外占用,-Xmx512m 时进程 RSS 可达 640 MB 属正常。
8.5 升级后页面样式错乱
2.27.1 修复了「升级后登录/注册页仍使用旧静态资源」的问题。若仍出现,清浏览器缓存并确认反向代理没有缓存 HTML:
curl -sI http://127.0.0.1:8090/login | grep -i cache
实测回显:
Cache-Control: no-cache, no-store, max-age=0, must-revalidate
8.6 数据库连接失败
检查数据库服务、账号权限与 spring.sql.init.platform 是否与 URL 匹配:
sudo systemctl is-active postgresql
sudo -u postgres psql -d halo -c "SELECT 1;"
实测回显:
active
?column?
----------
1
(1 row)
九、卸载与清理
sudo systemctl stop halo
sudo systemctl disable halo
sudo rm -f /etc/systemd/system/halo.service
sudo systemctl daemon-reload
sudo rm -rf /opt/halo /usr/local/jdk-21.0.13 /usr/local/java
sudo rm -rf /root/.halo2
若安装了 PostgreSQL 且不再使用:
sudo systemctl stop postgresql
sudo apt purge -y postgresql postgresql-client
sudo rm -rf /var/lib/postgresql /etc/postgresql
⚠️ 注意:删除
/root/.halo2会同时删除附件、主题、插件与 H2 数据库文件,操作前先备份。
十、参考链接
- Halo 官方文档:https://docs.halo.run
- 使用 JAR 文件部署:https://docs.halo.run/guide/install/jar-file
- 配置说明:https://docs.halo.run/guide/install/config
- 2.27.1 发布说明:https://github.com/halo-dev/halo/releases/tag/v2.27.1
- Temurin 下载:https://adoptium.net/temurin/releases/