Ubuntu 22.04 安装 Halo 2.27.1 指南

Ubuntu 22.04 裸机安装 Halo 2.27.1 全流程:Temurin JDK 21 + halo.jar + systemd 托管,H2 快速起步与外接 PostgreSQL 两套方案,含初始化、验证回显、JVM 内存限定、常见问题与卸载。

HaloHaloCMS建站JavaSpring Boot2026/10/27

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.02026-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.dbpg_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 数据库文件,操作前先备份。

十、参考链接