Halo 个人博客部署指南
目录
一、引言:为什么选择 Halo
如果你一直想拥有一个完全属于自己的网站——写技术笔记、记录生活、沉淀作品,那么 Halo 几乎是目前最适合个人开发者的选择之一。
Halo 是什么
Halo 是一款使用 Java(基于 Spring Boot 框架)编写的开源博客建站系统。所谓"开源",即源代码完全公开,任何人都可以免费使用、自由修改和二次分发。与 WordPress 等老牌系统相比,Halo 更年轻、更现代,也更适合中文用户。
核心优势
| 优势 | 说明 |
|---|---|
| 轻量级 | 单 Jar 包即可运行,最低 1 核 1GB 内存的小规格云服务器也能流畅部署 |
| 高性能 | 基于 Spring Boot 构建,启动快、资源占用低 |
| 开源免费 | MIT/GPL 友好许可,无需任何授权费用 |
| 插件生态丰富 | 评论、统计、友链、RSS 等功能均可通过插件按需安装 |
| 主题市场 | 后台一键安装精美主题,无需手动上传文件 |
| 管理界面友好 | 开箱即用的现代化后台(Console),Markdown 写作体验流畅 |
目标读者
本教程面向希望快速搭建个人站点的开发者或技术爱好者:你只需要会最基础的命令行操作(复制粘贴命令即可),无需任何 Java 开发经验。全文以当前主流的 Halo 2.x 版本为准进行讲解。
版本说明:Halo 1.x 是旧版本,仅需 JDK 11 即可运行,但官方已停止维护,不建议新项目使用。Halo 2.x 要求 JDK 17+,架构全面重构(插件化、主题市场等),本教程全部基于 2.x。
二、环境准备
2.1 前置条件清单
在开始之前,请确认你已经具备以下条件:
- 一台可长期运行的机器:云服务器(阿里云 / 腾讯云 / 华为云等,推荐)或家用 Linux 服务器 / NAS(Network Attached Storage,家用网络存储设备,可兼作小型服务器)
- 操作系统:推荐 Linux(Ubuntu 22.04+ / Debian 12+ / CentOS Stream 9 等主流发行版);Windows、macOS 同样支持,但生产环境首推 Linux
- 一个可以远程连接服务器的终端工具(如 Windows Terminal、PuTTY、Xshell)
- (可选)一个已备案或海外注册的域名,用于第五章的域名绑定与 HTTPS
2.2 硬件建议
| 配置项 | 最低要求 | 推荐配置 |
|---|---|---|
| CPU | 1 核 | 2 核及以上 |
| 内存 | 1 GB(建议开启 Swap) | 2 GB 及以上(搭配 PostgreSQL 时尤其重要) |
| 磁盘 | 10 GB | 40 GB 及以上(图片附件较多时) |
| 带宽 | 1 Mbps | 3 Mbps 及以上 |
💡 内存只有 1GB 的轻量服务器也能跑 Halo,但如果同时运行数据库,建议配置 Swap 交换分区作为缓冲,否则可能出现内存不足(OOM)导致进程被系统杀掉,详见 FAQ 第 6.5 节。
2.3 软件依赖:两种部署方式的区别
Halo 提供两种主流部署方式,依赖完全不同,请先选定一种再看对应小节:
| 部署方式 | 需要安装 Java 吗 | 需要安装的软件 | 适用人群 |
|---|---|---|---|
| 方式一:Docker 部署(推荐) | ❌ 不需要 | Docker + Docker Compose | 绝大多数用户,升级/迁移最方便 |
| 方式二:Jar 包直接运行 | ✅ 需要 JDK 17+ | JDK 17+(可选 systemd 做服务管理) | 有 Java 基础、希望精细控制进程的用户 |
检查 Java 版本(仅 Jar 包部署需要)
在终端执行:
java -version
正确输出示例(版本号 ≥ 17 即可):
openjdk version "17.0.11" 2024-04-16
OpenJDK Runtime Environment (build 17.0.11+9)
如果提示 command not found 或版本低于 17,请先安装 JDK 17+:
# Ubuntu / Debian
sudo apt update
sudo apt install -y openjdk-17-jdk
# CentOS / Rocky Linux
sudo dnf install -y java-17-openjdk
安装完成后再次执行 java -version 确认。
安装 Docker 与 Docker Compose(仅 Docker 部署需要)
# 一键安装 Docker(官方脚本,适用于主流 Linux 发行版)
# 该方式会执行远程官方脚本,请确认来源为 get.docker.com 官方域名后再运行
curl -fsSL https://get.docker.com | sh
# 启动并设置开机自启
sudo systemctl enable --now docker
# 验证安装
docker --version
docker compose version # 新版 Docker 已内置 compose 子命令
Docker 是一种容器技术,可以把 Halo 及其运行环境整体打包,避免"在我电脑上能跑"的依赖问题;Docker Compose 则是用于编排多个容器(例如 Halo + 数据库)的配置工具。
2.4 检查端口占用
Halo 默认使用 8090 端口。部署前先确认该端口未被占用:
sudo ss -lntp | grep 8090
无输出即代表端口空闲。若已被占用,处理方式见 FAQ 第 6.1 节。
三、安装步骤(核心)
本章提供两种部署方式,任选其一即可,新手强烈建议选方式一(Docker)。
方式一:Docker 部署(推荐)
3.1 使用 docker run 快速启动
Halo 官方在 Docker Hub 发布的镜像为 halohub/halo:2.x。以下以 2.20 为例,实际请使用 Docker Hub 上的最新稳定 tag(也可参考 官方文档):
⚠️ 安全提示:
-p 8090:8090默认绑定宿主机的所有网卡,一旦云厂商安全组放行了 8090 端口,你的初始化页面和/console后台即对全网可见,可能被他人抢先完成初始化或对后台账号发起暴力破解。请务必在部署后第一时间完成初始化并设置强密码(详见 4.1 节)。
docker run \
-d \
--name halo \
-p 8090:8090 \
-v ~/.halo2:/root/.halo2 \
--restart=unless-stopped \
halohub/halo:2.20
关键参数说明:
-d:后台运行容器(detach 模式),不占用当前终端。--name halo:容器命名为 halo,方便后续用docker logs halo等命令管理。-v ~/.halo2:/root/.halo2:这是最重要的一行。Halo 的所有数据(文章、附件、配置、内置 H2 数据库文件,H2 是一种无需额外安装的嵌入式轻量数据库)都保存在容器内/root/.halo2,挂载后即使删除容器,数据依然保留在宿主机的~/.halo2目录中。备份博客 = 备份这个目录。-p 8090:8090:端口映射,宿主机 8090 转发到容器内 8090。如果宿主机 8090 端口被占用,可改为-p 9090:8090(仅改冒号左侧),之后通过http://IP:9090访问。--restart=unless-stopped:重启策略,除手动停止外,开机或异常退出后自动拉起容器。halohub/halo:2.20:使用的官方镜像及版本标签。
💡 官方镜像内的进程以 root 身份运行,并且通过
-v挂载了宿主机目录,请注意宿主机~/.halo2目录的访问权限,避免被其他本地用户读取。
启动后执行以下命令确认容器正常运行:
docker ps # 查看容器状态,STATUS 应为 Up
docker logs -f halo # 实时查看启动日志,看到 Halo started 字样即成功,按 Ctrl+C 退出
3.2 使用 Docker Compose 编排 Halo + PostgreSQL(生产推荐)
Halo 2.x 内置 H2 数据库,开箱即用,适合个人轻量使用;但生产环境更推荐 PostgreSQL 或 MySQL,稳定性和性能更有保障。下面给出 Halo + PostgreSQL 的完整编排示例。
数据库是用来存储文章、用户、评论等数据的组件;Halo 通过环境变量决定连接哪个数据库,切换时无需修改代码。
在任意目录(例如 ~/halo)创建 docker-compose.yml 文件:
services:
halo:
image: halohub/halo:2.20 # 官方镜像,以下以 2.20 为例,实际请使用 Docker Hub 上的最新稳定 tag
container_name: halo
restart: unless-stopped # 重启策略:开机与异常退出自动拉起
depends_on:
halo-db: # 依赖数据库服务先启动
condition: service_healthy
networks:
- halo-network
volumes:
- ~/.halo2:/root/.halo2 # 数据持久化目录(关键:备份即备份此目录)
environment:
- JVM_OPTS=-Xmx512m # JVM 最大堆内存,1GB 内存的服务器建议 256m~512m
- SPRING_R2DBC_URL=r2dbc:pool:postgresql://halo-db:5432/halo
# ↑ 数据库连接地址(关键配置):r2dbc:pool:postgresql:// 为完整的驱动 + 连接池前缀,主机名使用 Compose 内部服务名 halo-db
- SPRING_SQL_INIT_PLATFORM=postgresql
# ↑ 告知 Halo 按 PostgreSQL 方言初始化数据库结构,与 R2DBC URL 配套
- SPRING_R2DBC_USERNAME=halo
# ↑ 数据库用户名(关键配置):必须与下方 POSTGRES_USER 一致
- SPRING_R2DBC_PASSWORD=你的强密码
# ↑ 数据库密码(关键配置):必须与下方 POSTGRES_PASSWORD 一致,请使用强密码
ports:
- "8090:8090" # 对外暴露端口,被占用时仅修改冒号左侧即可
halo-db:
image: postgres:16 # PostgreSQL 数据库
container_name: halo-db
restart: unless-stopped
networks:
- halo-network
volumes:
- ./db-data:/var/lib/postgresql/data # 数据库数据持久化(关键:同样需要备份)
environment:
- POSTGRES_PASSWORD=你的强密码 # 数据库密码,与上方 SPRING_R2DBC_PASSWORD 保持一致
- POSTGRES_USER=halo # 数据库用户
- POSTGRES_DB=halo # 数据库名
healthcheck:
test: ["CMD", "pg_isready", "-U", "halo", "-d", "halo"]
interval: 10s
timeout: 5s
retries: 5
networks:
halo-network: # 自定义网络:让 halo 与 halo-db 互相隔离并可按服务名访问
driver: bridge
volumes: {} # 本示例使用宿主机路径挂载,如需命名卷可在此声明
关键配置项加粗说明:
SPRING_R2DBC_URL/SPRING_R2DBC_USERNAME/SPRING_R2DBC_PASSWORD:Halo 通过这三个环境变量切换到 PostgreSQL。若改用 MySQL,则替换为对应的SPRING_DATASOURCE_URL、SPRING_DATASOURCE_USERNAME、SPRING_DATASOURCE_PASSWORD(具体变量名以官方文档为准)。不配置任何数据库变量时,Halo 默认使用内置 H2 数据库。- 两个 volumes 挂载:
~/.halo2(应用数据)与./db-data(数据库数据)共同构成完整备份范围。 depends_on + healthcheck:确保数据库真正就绪后 Halo 才启动,避免启动时连接失败。networks: halo-network:自定义 bridge 网络使容器间可通过服务名(halo-db)互相访问,且与宿主机其他容器隔离。
⚠️ 密码安全提示:上方 compose 文件中是明文密码,仅为方便演示。生产环境建议将密码移入同目录的
.env文件(例如写成POSTGRES_PASSWORD=${DB_PASSWORD}、SPRING_R2DBC_PASSWORD=${DB_PASSWORD},Compose 会自动读取),并执行chmod 600 .env限制读取权限。切勿将含密码的 compose / .env 文件粘贴到论坛、截图或提交到公开代码仓库。
启动编排:
cd ~/halo
docker compose up -d # 后台启动所有服务
docker compose ps # 确认两个容器均为 Up (healthy) / Up 状态
docker compose logs -f halo # 跟踪 Halo 启动日志
若你暂时不想折腾数据库,也可以删掉
halo-db服务及 halo 服务中的三个SPRING_R2DBC_*环境变量与depends_on,直接使用内置 H2 数据库,后续再通过备份迁移。
方式二:Jar 包直接运行
3.3 下载官方 Jar 包
- 打开 Halo 官方发布页:https://github.com/halo-dev/halo/releases
- 找到最新的
2.x稳定版本(避免选择带-rc、-beta后缀的预览版) - 在发布页的 Assets 区域下载以
.jar结尾的构建产物(例如halo-2.20.0.jar,注意实际文件名包含完整补丁号)
服务器上可以使用 wget 直接下载(请将链接替换为发布页中的实际地址):
# 创建工作目录
mkdir -p /opt/halo && cd /opt/halo
# 下载 jar 包(以下 URL 请以 GitHub Releases 页面实际链接为准)
wget https://github.com/halo-dev/halo/releases/download/v2.20.0/halo-2.20.0.jar -O halo.jar
3.4 前台启动验证
先手动启动一次,确认环境没有问题:
cd /opt/halo
java -jar halo.jar
看到类似 Halo started successfully 的日志后,浏览器访问 http://服务器IP:8090 应能看到初始化页面。验证成功后按 Ctrl+C 停止,接下来配置 systemd 实现后台常驻。
💡 如需更换端口或指定数据目录,可通过启动参数实现,例如:
java -jar halo.jar --server.port=9090 --halo.work-dir=/data/halo2。
3.5 使用 systemd 管理后台服务
systemd 是 Linux 的系统服务管理器,可以把 Halo 注册为系统服务,实现开机自启、崩溃自动重启。
创建服务文件 /etc/systemd/system/halo.service:
sudo nano /etc/systemd/system/halo.service
写入以下内容(注意将 User 替换为你的实际运行用户,jar 路径与 WorkingDirectory 保持一致):
[Unit]
Description=Halo Blog Platform
Documentation=https://docs.halo.run
After=network-online.target
Wants=network-online.target
[Service]
User=www # 运行用户(建议新建非 root 用户)
Group=www
WorkingDirectory=/opt/halo # 工作目录:Halo 默认数据目录 ~/.halo2 将位于该用户家目录下
ExecStart=/usr/bin/java -Xmx512m -jar /opt/halo/halo.jar # 启动命令(关键)
Restart=on-failure # 异常退出后自动重启(关键)
RestartSec=5
SuccessExitStatus=143 # 正常停止信号不计为失败
LimitNOFILE=65536
[Install]
WantedBy=multi-user.target
重载 systemd 配置并管理服务:
sudo systemctl daemon-reload # 重载服务文件
sudo systemctl enable halo # 设置开机自启
sudo systemctl start halo # 立即启动
sudo systemctl status halo # 查看运行状态(显示 active (running) 即成功)
日常管理速查:
sudo systemctl stop halo # 停止
sudo systemctl restart halo # 重启
journalctl -u halo -f # 实时查看日志
⚠️ Jar 包方式下,数据默认保存在运行用户的
~/.halo2目录,备份策略与 Docker 方式一致。
四、基础配置
4.1 首次访问与初始化
安装完成后,在浏览器打开:
http://你的服务器IP:8090
首次访问会自动进入初始化界面,需要创建管理员账号:
- 填写站点名称(即博客名字,之后可在后台修改)
- 填写管理员用户名(登录后台使用)
- 填写邮箱与密码(密码建议 12 位以上,含大小写与数字)
- 点击初始化按钮,等待跳转
⚠️ 安全提示:初始化时必须设置强密码(建议 12 位以上、含大小写字母与数字)。公网 HTTP 阶段请避免在后台进行敏感操作,建议尽快完成第五章的域名与 HTTPS 配置。
初始化完成后,前台地址为
http://IP:8090,后台(Console)地址固定为http://IP:8090/console。所谓后台,就是管理文章、主题、插件的控制中心。
4.2 登录后台
访问 http://你的服务器IP:8090/console,输入刚才创建的用户名与密码登录:
4.3 设置站点基本信息
登录后按以下路径完成基础设置:左侧菜单 → 系统设置 → 基本设置。
逐项操作:
- 站点名称:填写博客名称,例如"小明的技术笔记"
- 站点副标题:一句话介绍,例如"记录学习与生活"
- Logo 上传:点击上传区域选择本地图片(建议正方形 PNG,512×512 以上),上传后即时预览
- 站点地址(URL):暂时保持默认,第五章绑定域名后务必回来改为正式域名,否则附件链接可能仍指向 IP
- 点击右上角保存
4.4 选择并安装主题
Halo 的主题(决定博客前台长相)可以直接在后台市场安装,无需手动下载上传:左侧菜单 → 外观 → 主题,切换到主题市场标签页浏览并点击安装。
适合新手的热门推荐:
| 主题 | 特点 |
|---|---|
| Theme Earth | Halo 2.x 官方默认主题,稳定、文档齐全,建议先用它熟悉流程 |
| Dream | 现代简约风,深色模式支持良好 |
| Joe | 功能丰富、可定制项多,社区活跃 |
安装完成后点击启用,再访问 http://IP:8090 即可看到新外观。主题的细节(首页布局、配色、侧边栏)通常在 外观 → 主题设置 中调整。
4.5 写下第一篇文章
路径:左侧菜单 → 文章 → 新建文章。编辑器支持标准 Markdown 语法,写完点击发布,前台首页即可看到。
4.6 建议顺手安装的插件
在 插件市场 中,以下插件对新手非常实用:评论组件、站点统计、RSS 订阅、友情链接管理。安装与启用方式和主题一样,一键完成。
💡 完成本章配置后,建议现在就先把
~/.halo2目录备份一次(Jar 包方式为运行用户的家目录),养成备份习惯。
五、域名绑定与 HTTPS
用 IP 访问虽然可行,但既难记又无法启用 HTTPS 加密。本章带你完成域名绑定与证书配置。
名词解释:
- DNS 解析:把人类可读的域名(如 blog.example.com)翻译成服务器 IP 地址的系统,相当于互联网的"电话簿"。
- 反向代理:由一台服务器(这里用 Nginx/Caddy)统一接收外部请求,再转发给内网中真正提供服务的程序(Halo 的 8090 端口),对外隐藏真实端口与架构。
- SSL 证书:让网站启用 HTTPS 加密传输的数字证书,浏览器会显示安全锁标志;Let's Encrypt 是最常用的免费证书颁发机构。
5.1 DNS 解析
- 登录你的域名服务商控制台(阿里云 DNS、腾讯云 DNSPod、Cloudflare 等)
- 添加一条 A 记录(把域名直接指向某个 IP 地址的 DNS 记录类型):
- 主机记录:
blog(或直接@表示主域名) - 记录类型:
A - 记录值:你的服务器公网 IP
- TTL:默认 600 即可
- 主机记录:
- 等待解析生效(通常几分钟内),可用以下命令验证:
ping blog.example.com # 返回的 IP 应为你的服务器 IP
若你的博客托管在对象存储或负载均衡等场景(个人博客一般用不到),也可使用 CNAME 记录指向服务商提供的域名;个人服务器场景一律使用 A 记录最简单。
解析生效后,访问 http://blog.example.com:8090 应能看到博客。接下来的目标是去掉 :8090 并启用 HTTPS。
5.2 安装 Nginx
# Ubuntu / Debian
sudo apt update && sudo apt install -y nginx
# CentOS / Rocky
sudo dnf install -y nginx
sudo systemctl enable --now nginx
5.3 Nginx 反向代理配置(HTTP 阶段)
创建站点配置文件 /etc/nginx/conf.d/halo.conf:
server {
listen 80; # 监听 HTTP 80 端口(证书申请阶段需要)
server_name blog.example.com; # 替换为你的域名(关键配置)
# 证书验证路径:供 5.4 节 acme.sh webroot 方式使用,务必放在 location / 之前(关键配置)
location ^~ /.well-known/acme-challenge/ {
root /var/www/html;
}
location / {
proxy_pass http://127.0.0.1:8090; # 转发到本机 Halo 服务(关键配置)
proxy_set_header Host $host; # 传递原始域名,Halo 依赖它生成正确链接
proxy_set_header X-Real-IP $remote_addr; # 传递真实客户端 IP
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme; # 传递协议类型 http/https(关键配置)
proxy_http_version 1.1;
}
}
关键配置项说明:
proxy_pass http://127.0.0.1:8090:所有请求转发给本机 Halo;若 Halo 部署在其他机器或改了端口,在此调整。X-Forwarded-Proto:让 Halo 知道原始请求是 HTTP 还是 HTTPS,否则启用 HTTPS 后可能出现链接与跳转异常。
测试并重载配置:
sudo nginx -t # 语法检查,显示 syntax is ok 再继续
sudo systemctl reload nginx # 平滑重载,不中断现有连接
此时访问 http://blog.example.com(无需端口)即可看到博客。
5.4 使用 acme.sh 申请 Let's Encrypt 证书
推荐使用 acme.sh(纯 Shell 脚本,自动续期)。
⚠️ 请使用 root(或 sudo -i 切换到 root)身份安装与使用 acme.sh:后续
--reloadcmd "systemctl reload nginx"需要 root 权限,否则证书自动续期时会因无权重载 Nginx 而静默失败,新证书不生效。
以下演示 webroot 方式(验证目录已在上一步 5.3 节配置好 /.well-known/acme-challenge/ 路径,无需停止 Nginx):
# 安装 acme.sh(将邮箱替换为你自己的,用于续期通知)
# 该方式会执行远程官方脚本,请确认来源为 get.acme.sh 官方域名后再运行
curl https://get.acme.sh | sh -s email=you@example.com
# 签发证书:-w 指定验证目录,与 5.3 节配置的 root 路径保持一致
~/.acme.sh/acme.sh --issue -d blog.example.com -w /var/www/html
如果站点根目录不便使用,也可改用 standalone 方式(acme.sh 临时占用 80 端口完成验证,需先停止 Nginx):
sudo systemctl stop nginx
sudo ~/.acme.sh/acme.sh --issue -d blog.example.com --standalone
sudo systemctl start nginx
💡 无论签发成功与否,请务必确认已重新执行
sudo systemctl start nginx恢复站点访问。
证书签发成功后,将证书安装(部署)到 Nginx 使用的路径,并配置自动重载:
sudo ~/.acme.sh/acme.sh --install-cert -d blog.example.com \
--key-file /etc/nginx/ssl/blog.example.com.key \
--fullchain-file /etc/nginx/ssl/blog.example.com.pem \
--reloadcmd "systemctl reload nginx"
# 收紧私钥权限,仅允许 root 读取
sudo chmod 600 /etc/nginx/ssl/blog.example.com.key
🔒 私钥(
.key文件)等同于站点的身份凭证,一旦泄露他人可冒充你的站点,禁止上传、分享或提交到代码仓库。
最后验证一次续期链路是否可用(模拟定时任务触发,无报错即正常):
sudo ~/.acme.sh/acme.sh --cron
证书有效期 90 天,acme.sh 安装时已自动写入 crontab(Linux 的定时任务调度器)定时续期,
--reloadcmd会在续期后自动执行systemctl reload nginx,无需人工干预。
也可以使用 certbot 作为替代:
sudo apt install -y certbot python3-certbot-nginx && sudo certbot --nginx -d blog.example.com,certbot 会自动改写 Nginx 配置。
5.5 Nginx 启用 HTTPS
将 5.3 节的配置升级为完整的 HTTP 跳转 + HTTPS 站点:
# HTTP → HTTPS 强制跳转
server {
listen 80;
server_name blog.example.com;
return 301 https://$host$request_uri;
}
# HTTPS 主站点
server {
listen 443 ssl;
server_name blog.example.com; # 替换为你的域名
ssl_certificate /etc/nginx/ssl/blog.example.com.pem; # 证书文件(acme.sh 部署的)
ssl_certificate_key /etc/nginx/ssl/blog.example.com.key; # 私钥文件
ssl_protocols TLSv1.2 TLSv1.3;
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;
proxy_http_version 1.1;
}
}
sudo nginx -t && sudo systemctl reload nginx
最后回到 Halo 后台:系统设置 → 基本设置,将站点地址改为 https://blog.example.com,保存即可。浏览器地址栏出现安全锁,说明 HTTPS 配置成功。
5.6 替代方案:Caddy 极简自动 HTTPS
如果你不想手动管理证书,Caddy 是更省心的选择——它默认自动申请并续期 Let's Encrypt 证书,配置只需两行。
# Ubuntu / Debian 安装 Caddy(官方 apt 源)
sudo apt install -y debian-keyring debian-archive-keyring apt-transport-https curl
curl -1sLf 'https://dl.cloudsmith.io/public/caddy/stable/gpg.key' | sudo gpg --dearmor -o /usr/share/keyrings/caddy-stable-archive-keyring.gpg
curl -1sLf 'https://dl.cloudsmith.io/public/caddy/stable/debian.deb.txt' | sudo tee /etc/apt/sources.list.d/caddy-stable.list
sudo apt update && sudo apt install -y caddy
编辑 /etc/caddy/Caddyfile:
blog.example.com {
reverse_proxy 127.0.0.1:8090
}
sudo systemctl reload caddy
首次访问域名时,Caddy 会自动完成证书申请(确保域名解析已生效且 80/443 端口已在安全组放行)。Nginx 与 Caddy 二选一即可,不要同时监听 80/443 端口。
六、常见问题排查 FAQ
6.1 端口 8090 被占用
现象:启动时报 Port 8090 was already in use,或容器端口映射失败。
排查思路:先确认谁占用了端口。
sudo ss -lntp | grep 8090 # 查看占用进程名与 PID
sudo netstat -tunlp | grep 8090 # 备选命令
解决方案:
- 若占用进程可停止(如残留的旧 Halo 实例),
sudo kill <PID>后重启即可 - 若必须共存,则更换端口:
- Docker:
-p 9090:8090(仅改冒号左侧),Compose 中改ports: - "9090:8090" - Jar 包:
java -jar halo.jar --server.port=9090,并同步修改 systemd 文件中的ExecStart后执行sudo systemctl daemon-reload && sudo systemctl restart halo - 使用 Nginx/Caddy 反代时,同步把
proxy_pass/reverse_proxy指向新端口
- Docker:
6.2 数据库连接失败(PostgreSQL / MySQL)
现象:Halo 启动日志报 Failed to obtain database connection 等连接异常。
排查思路:按"配置 → 网络 → 密码"顺序逐一核对。
- 核对环境变量:
SPRING_R2DBC_URL中的主机名、端口、库名必须与数据库服务一致;Compose 内必须使用服务名(如halo-db)而非localhost - 核对账号密码:
SPRING_R2DBC_USERNAME/PASSWORD必须与POSTGRES_USER/POSTGRES_PASSWORD完全一致,注意特殊字符(如$、#)在 Compose 中需要转义 - 验证网络连通:进入 Halo 容器测试数据库端口
# 在数据库容器侧验证连通性(Halo 官方镜像未预装 netcat,故从数据库侧检查)
docker compose exec halo-db pg_isready -U halo -d halo # 显示 accepting connections 即就绪
docker compose logs halo-db # 查看数据库侧日志与报错
解决方案:修正配置后执行 docker compose down && docker compose up -d 重建;若数据库已初始化过账号,修改密码需要同时处理数据库内已有用户(或清空 db-data 重新初始化,注意会丢失数据)。
6.3 目录权限问题(挂载卷 / 非 root 运行)
现象:容器启动即退出,日志出现 Permission denied、无法写入 /root/.halo2 等错误;或 Jar 包方式下 systemd 服务启动失败。
排查思路:挂载目录的属主/权限与容器内运行用户不匹配是最常见原因。
ls -ld ~/.halo2 # 查看目录属主与权限
docker logs halo # 确认具体报错路径
解决方案:
- 调整挂载目录权限(简单场景):
sudo chown -R 1000:1000 ~/.halo2 # 或将属主改为当前运行用户:chown -R $USER ~/.halo2
sudo chmod -R u+rwX ~/.halo2
- systemd 方式下,确保
halo.service中的User对WorkingDirectory与其家目录下的.halo2有读写权限;生产环境强烈建议以非 root 用户运行,降低安全风险 - 修改后重启容器或服务:
docker compose restart halo/sudo systemctl restart halo
6.4 云服务器安全组未放行端口
现象:服务器本地 curl http://127.0.0.1:8090 正常,但外网浏览器始终无法访问(连接超时)。
排查思路:请求根本没到达服务器,通常是云厂商安全组(云平台层面的虚拟防火墙)或系统防火墙未放行端口。注意:绑定域名并使用 Nginx/Caddy 后,对外暴露的是 80/443,而不是 8090。
解决方案:
- 登录云厂商控制台(阿里云/腾讯云/华为云等),在实例的安全组中添加入方向规则:
- 仅用 IP 访问 Halo:放行 TCP 8090
- 已配置 Nginx/Caddy:强烈建议在安全组中删除或限制 8090 的放行规则,仅保留 TCP 80 与 443;若使用 docker run 部署,可进一步将端口映射改为仅绑定本机回环地址
-p 127.0.0.1:8090:8090,这样 8090 完全不对公网暴露,只能由本机 Nginx 反代访问
- 检查系统防火墙:
sudo ufw status # Ubuntu:如已启用,执行 sudo ufw allow 8090/tcp
sudo firewall-cmd --list-ports # CentOS:如已启用,执行 sudo firewall-cmd --add-port=8090/tcp --permanent && sudo firewall-cmd --reload
- 规则生效后等待约 10 秒再次访问验证
6.5 内存不足导致容器被杀(OOM)
现象:博客运行一段时间后突然无法访问,docker ps 显示容器反复重启或已退出;docker inspect halo 中可见 OOMKilled: true;系统日志出现 Out of memory: Killed process ... java。
排查思路:1GB 内存的服务器同时运行 Halo + PostgreSQL 时容易触发内核 OOM Killer。
free -h # 查看可用内存与 Swap
docker inspect halo | grep -i oom # 确认是否 OOMKilled
dmesg | grep -i "killed process" | tail # 查看系统 OOM 记录
解决方案:
- 限制并降低 JVM 堆内存:Compose 中设置
JVM_OPTS=-Xmx256m(或 512m),避免 Java 无限申请内存 - 开启 Swap(1GB 服务器的救命稻草)。执行前先用
swapon --show确认是否已有 Swap,若已配置请跳过本步:
swapon --show # 先检查:有输出则说明已存在 Swap,跳过以下步骤
sudo fallocate -l 2G /swapfile
sudo chmod 600 /swapfile
sudo mkswap /swapfile && sudo swapon /swapfile
echo '/swapfile none swap sw 0 0' | sudo tee -a /etc/fstab
- 如预算允许,升级至 2GB+ 内存是最根本的解法;也可考虑只使用内置 H2 数据库以减少一个常驻进程
6.6 容器陷入重启循环
现象:docker ps 中 STATUS 显示 Restarting (1) x seconds ago,反复重启无法稳定运行。
排查思路:容器是"启动 → 崩溃 → 被 restart 策略拉起 → 再崩溃"的循环,关键是看崩溃前的日志。
docker logs --tail 200 halo # 查看最近 200 行日志
docker logs -f halo # 实时跟踪一次完整的启动崩溃过程
docker inspect halo | grep -A3 State # 查看退出码 ExitCode
解决方案:按日志报错对症处理——
- 端口冲突 → 参考 6.1
- 数据库连接失败 → 参考 6.2
- 权限拒绝 → 参考 6.3
- 镜像版本损坏或标签异常 → 删除本地镜像重新拉取:
docker rmi halohub/halo:2.20 && docker compose pull - 配置实在无法定位时,可临时移除
--restart策略让容器"停住",便于从容排查
💡 通用排查三件套:
docker ps(看状态)、docker logs(看日志)、docker inspect(看配置与退出码)。绝大多数容器问题都能靠这三条命令定位。
七、结语
恭喜你走完了全程!回顾一下我们完成的事情:
- 环境准备:确认了系统、硬件与依赖——Docker 部署只需 Docker,Jar 包部署需要 JDK 17+
- 安装部署:通过
docker run/ Docker Compose(Halo + PostgreSQL)或 Jar 包 + systemd 启动了 Halo,数据统一持久化在~/.halo2 - 基础配置:在
http://IP:8090完成初始化,在/console后台设置了站点信息并安装了主题 - 域名与 HTTPS:通过 DNS 解析 + Nginx 反向代理 + Let's Encrypt 证书(或 Caddy 自动 HTTPS)让博客拥有了安全锁
接下来最值得做的三件事:
- 备份:把
~/.halo2(使用数据库时还有数据目录)纳入定期备份计划,数据才是博客最值钱的资产 - 写作:技术博客的生命力在于持续输出,先写三篇再谈优化
- 探索生态:在后台的插件市场与主题市场逛逛,按需增强你的站点
工具只是起点,内容才是灵魂。现在就打开 http://你的域名/console,写下第一篇文章吧!
📌 本文基于 Halo 2.x 撰写,镜像版本、环境变量等细节可能随官方迭代变化,实际操作请以 Halo 官方文档 与 GitHub Releases 为准。