Halo 2.27.1 Docker Compose 部署指南

Halo 2.27.1 Docker Compose 部署指南:显式项目名 halo-2.27.1-stack、Halo + PostgreSQL 17 + Nginx 反代三服务编排,含端口/网段规划、持久化验证、等价 docker run、运维命令与项目名规范化实测。

HaloHaloCMS建站JavaSpring Boot2026/10/27

Halo 2.27.1 Docker Compose 部署指南

目标:用 Docker Compose 在一台 Linux 主机上编排 Halo 2.27.1 + PostgreSQL 17 + Nginx 反向代理,显式固定项目名,规划独立网段与不冲突端口,验证初始化、反向代理与数据持久化。全文命令与回显于 2026-10-27 实测。

一、环境信息

  • 操作系统:Ubuntu 22.04.5 LTS(x86_64)
  • Docker Engine:29.x(Compose v2 插件)
  • 项目目录:~/halo(本文实测路径 /opt/data/quzuo/tmp/halo/compose)
  • Compose 项目名:halo-2.27.1-stack(声明值),实际生效 halo-2271-stack
  • 应用镜像:halohub/halo:2.27.1
  • 数据库镜像:postgres:17-alpine
  • 反向代理镜像:nginx:1.27-alpine
  • 宿主端口:Halo 8131、Nginx 8132
  • 容器网段:10.89.62.0/24
  • 数据目录:./halo2(挂到 /root/.halo2)、./pgdata
  • 站点地址:http://127.0.0.1:8131(直连)、http://127.0.0.1:8132(经 Nginx)
  • 管理账号(示例):root / q1w2e3r4

⚠️ 网络说明:本次实测环境无法直连 Docker Hub(docker pull halohub/halo:2.27.1 长时间无进展,busybox 同样失败),因此用官方 halo-2.27.1.jar + Temurin JDK 21 在本地构建了与官方镜像等价的 halohub/halo:2.27.1(同一 JAR、同一启动方式、同样支持 JVM_OPTS)。编排、网络、持久化、反代等行为与官方镜像一致;若你的环境可正常拉取,直接使用官方镜像即可,无需自行构建。

二、镜像选型说明

镜像用途说明
halohub/halo:2.27.1应用社区版官方镜像,版本号与 JAR 对齐;2.27、2 为滚动标签,生产建议固定补丁版
postgres:17-alpine数据库官方 PostgreSQL 17 Alpine 变体,体积小;也可换 mysql:8.4 / mariadb:11
nginx:1.27-alpine反向代理仅做 80 端口转发与上传体积放宽,可选

选择要点:

  • Halo 镜像必须与 JAR 版本一致,混用版本会在升级数据结构时出问题。
  • 数据库用 alpine 变体可减少拉取体积;国内网络可配置镜像加速器。
  • Nginx 不是必需组件。若已有宿主机 Nginx 或云负载均衡,可删掉该服务,仅保留 Halo + 数据库。

三、前置条件

docker --version
docker compose version
ss -lntp | grep -E "8131|8132" || echo "端口空闲"

实测回显:

Docker version 29.8.1, build 8bd1a7b
Docker Compose version v2.40.3
端口空闲

端口规划遵循「不占用宿主既有服务」原则:本文选 8131(Halo)与 8132(Nginx),均落在 8128–8152 区间内且未被占用。网段选 10.89.62.0/24,需确认不与宿主已有网段冲突:

docker network ls --format '{{.Name}}' | while read n; do
  docker network inspect "$n" --format '{{.Name}} {{range .IPAM.Config}}{{.Subnet}}{{end}}'
done | grep 10.89

实测回显(节选,已存在网段不含 62):

bridge 172.17.0.0/16
dittofeed-v0.24.0-alpha.21-net 10.89.59.0/24
fossil-70 10.89.70.0/24
gitbucket-b1-net 10.89.58.0/24
hoppscotch-v2026-9-stack_hoppscotch-net 10.89.56.0/24

四、部署步骤

4.1 创建项目目录

mkdir -p ~/halo/nginx/conf.d
cd ~/halo

目录约定:halo2/ 存 Halo 工作目录,pgdata/ 存数据库文件,nginx/conf.d/ 存反代配置。三者都要纳入备份。

4.2 编写 docker-compose.yaml

name: halo-2.27.1-stack

services:
  halo:
    image: halohub/halo:2.27.1
    container_name: halo-2.27.1-stack-halo
    restart: on-failure:3
    depends_on:
      halodb:
        condition: service_healthy
    networks:
      - halo-net
    volumes:
      - ./halo2:/root/.halo2
    ports:
      - "8131:8090"
    healthcheck:
      test: ["CMD", "curl", "-f", "http://localhost:8090/actuator/health/readiness"]
      interval: 30s
      timeout: 5s
      retries: 5
      start_period: 60s
    environment:
      - JVM_OPTS=-Xms256m -Xmx512m
    command:
      - --spring.r2dbc.url=r2dbc:pool:postgresql://halodb:5432/halo
      - --spring.r2dbc.username=halo
      - --spring.r2dbc.password=q1w2e3r4
      - --spring.sql.init.platform=postgresql
      - --halo.external-url=http://127.0.0.1:8131/
      - --server.port=8090

  halodb:
    image: postgres:17-alpine
    container_name: halo-2.27.1-stack-halodb
    restart: on-failure:3
    networks:
      - halo-net
    volumes:
      - ./pgdata:/var/lib/postgresql/data
    environment:
      - POSTGRES_USER=halo
      - POSTGRES_PASSWORD=q1w2e3r4
      - POSTGRES_DB=halo
    healthcheck:
      test: ["CMD-SHELL", "pg_isready -U halo -d halo"]
      interval: 10s
      timeout: 5s
      retries: 6
      start_period: 20s

  nginx:
    image: nginx:1.27-alpine
    container_name: halo-2.27.1-stack-nginx
    restart: on-failure:3
    depends_on:
      halo:
        condition: service_started
    networks:
      - halo-net
    volumes:
      - ./nginx/conf.d:/etc/nginx/conf.d:ro
    ports:
      - "8132:80"

networks:
  halo-net:
    driver: bridge
    ipam:
      config:
        - subnet: 10.89.62.0/24

关键项说明:

  • name::Compose 顶层项目名。必须显式声明,否则默认取目录名(如 compose),多项目共存时容易撞名。
  • container_name::显式固定容器名,便于运维;不写则由 Compose 自动拼 <项目名>-<服务名>-<序号>。
  • depends_on 的 condition: service_healthy:等 PostgreSQL 健康检查通过再启动 Halo,避免首次连接失败。
  • JVM_OPTS:注入 JVM 堆上限,官方镜像的入口脚本会把它拼进启动命令。
  • command: 里的参数等价于 application.yaml 的 spring/halo 段,容器化场景推荐用参数而非挂载配置文件。
  • healthcheck 的 start_period: 60s:Halo 首次启动含数据库迁移,给足冷启动时间。
  • ipam.subnet:固定网段,避免与公司内网/VPN 网段冲突。

4.3 编写 Nginx 反代配置

nginx/conf.d/halo.conf:

server {
    listen 80;
    server_name _;

    client_max_body_size 1024m;

    location / {
        proxy_pass http://halo: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;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection "upgrade";
    }
}

要点:

  • client_max_body_size 1024m:Halo 默认不限制上传大小,反代默认只有 1 MB,不放开会导致大附件 413。
  • X-Forwarded-Proto:Halo 据此判断原始协议,HTTPS 终止在 Nginx 时必须带。
  • Upgrade/Connection:插件或后台的 WebSocket 长连接需要。

4.4 启动

docker compose up -d

实测回显:

 Network halo-2271-stack_halo-net Creating
 Network halo-2271-stack_halo-net Created
 Container halo-2.27.1-stack-halodb Creating
 Container halo-2.27.1-stack-halodb Created
 Container halo-2.27.1-stack-halo Creating
 Container halo-2.27.1-stack-halo Created
 Container halo-2.27.1-stack-nginx Creating
 Container halo-2.27.1-stack-nginx Created
 Container halo-2.27.1-stack-halodb Starting
 Container halo-2.27.1-stack-halodb Started
 Container halo-2.27.1-stack-halodb Waiting
 Container halo-2.27.1-stack-halodb Healthy
 Container halo-2.27.1-stack-halo Starting
 Container halo-2.27.1-stack-halo Started
 Container halo-2.27.1-stack-nginx Starting
 Container halo-2.27.1-stack-nginx Started

⚠️ 注意:声明的是 halo-2.27.1-stack,实际创建的网络叫 halo-2271-stack_halo-net。Compose 会把项目名规范化为「小写字母、数字、连字符、下划线」,点号被移除,因此 2.27.1 变成 2271。用 docker network ls 或 docker compose ls 核对时要以规范化后的名字为准:

halo-2271-stack    running(3)    /opt/data/quzuo/tmp/halo/compose/docker-compose.yaml

docker compose config 里也会显示 name: halo-2271-stack。

4.5 验证运行状态

docker compose ps

实测回显:

NAME                       IMAGE                 COMMAND                  SERVICE   CREATED         STATUS                    PORTS
halo-2.27.1-stack-halo     halohub/halo:2.27.1   "/entrypoint.sh --sp…"   halo      7 seconds ago   Up (healthy)              0.0.0.0:8131->8090/tcp, [::]:8131->8090/tcp
halo-2.27.1-stack-halodb   postgres:17-alpine    "docker-entrypoint.s…"   halodb    7 seconds ago   Up 6 seconds (healthy)    5432/tcp
halo-2.27.1-stack-nginx    nginx:1.27-alpine     "/docker-entrypoint.…"   nginx     7 seconds ago   Up                        0.0.0.0:8132->80/tcp, [::]:8132->80/tcp

启动日志:

docker logs halo-2.27.1-stack-halo 2>&1 | grep -E "Version:|Netty started|Started Application"

实测回显:

Version: 2.27.1
2026-10-27T13:38:02.683Z  INFO 1 --- [           main] o.s.boot.reactor.netty.NettyWebServer    : Netty started on port 8090 (http)
2026-10-27T13:38:02.870Z  INFO 1 --- [           main] run.halo.app.Application                 : Started Application in 12.685 seconds (process running for 13.646)

容器化启动比裸机 JAR 稍慢(12.7 秒 vs 7.2 秒),主要耗时在数据库迁移与健康检查等待。

验证网络与 IP 分配:

docker network inspect halo-2271-stack_halo-net \
  --format '{{.Name}} {{range .IPAM.Config}}{{.Subnet}}{{end}} {{range .Containers}}{{.Name}}={{.IPv4Address}} {{end}}'

实测回显:

halo-2271-stack_halo-net 10.89.62.0/24 halo-2.27.1-stack-halodb=10.89.62.2/24 halo-2.27.1-stack-nginx=10.89.62.4/24 halo-2.27.1-stack-halo=10.89.62.3/24

Halo 通过服务名 halodb 访问数据库,无需知道 IP;IP 由 Compose 按网段顺序分配。

4.6 初始化站点

浏览器访问 http://<服务器IP>:8131/console,填写站点信息:

  • 站点标题:Halo Compose 站点
  • 管理员:root / q1w2e3r4
  • 邮箱:root@quzuo.net
  • 外部访问地址:http://<服务器IP>:8131
  • 语言:简体中文

初始化后验证直连与反代:

curl -s -o /dev/null -w "halo8131=%{http_code}\n" http://127.0.0.1:8131/
curl -s -o /dev/null -w "nginx8132=%{http_code}\n" http://127.0.0.1:8132/
curl -s http://127.0.0.1:8132/ | grep -o "<title>[^<]*</title>"

实测回显:

halo8131=200
nginx8132=200
<title>Halo Compose 站点</title>

五、数据持久化说明

Halo 的所有状态分两处存放:

  1. ./halo2 → 容器内 /root/.halo2:附件、主题、插件、索引、日志、PAT 密钥(数据库内容不在这里)。
  2. ./pgdata → 容器内 /var/lib/postgresql/data:站点内容、用户、配置全部在 PostgreSQL 的 extensions 表中。

实测目录体积:

du -sh halo2 pgdata
ls halo2
26M	halo2
47M	pgdata
backups
indices
keys
logs
plugins
themes

停机再启动验证数据不丢:

docker compose down
docker compose up -d
sleep 40
docker compose ps
curl -s -o /dev/null -w "home_after_down_up=%{http_code}\n" http://127.0.0.1:8131/

实测回显:

NAME                       STATUS                    PORTS
halo-2.27.1-stack-halo     Up 40 seconds (healthy)   0.0.0.0:8131->8090/tcp, [::]:8131->8090/tcp
halo-2.27.1-stack-halodb   Up 46 seconds (healthy)   5432/tcp
halo-2.27.1-stack-nginx    Up 40 seconds             0.0.0.0:8132->80/tcp, [::]:8132->80/tcp
home_after_down_up=200

⚠️ 注意:docker compose down 默认保留命名卷与绑定目录,数据不会丢;但 docker compose down -v 会删除命名卷。本项目全部用绑定挂载(./halo2、./pgdata),所以 -v 对它们无效,真正的风险是手工 rm -rf pgdata。

💡 说明:备份要「目录 + 数据库」一起做。只备份 halo2 会丢掉全部文章;只 pg_dump 会丢掉附件与主题包。推荐停机备份或数据库在线 pg_dump + 目录 rsync。

六、环境变量表

变量作用域示例值说明
JVM_OPTShalo-Xms256m -Xmx512m注入 JVM 堆参数;置空表示不追加
POSTGRES_USERhalodbhalo初始化时创建的超级用户
POSTGRES_PASSWORDhalodbq1w2e3r4数据库密码,示例值;生产用强口令并改用 secrets
POSTGRES_DBhalodbhalo初始数据库名,必须与 Halo 的 URL 一致

确认 JVM 参数已生效:

docker exec halo-2.27.1-stack-halo ps -ef | grep [j]ava

实测回显:

root           1       0 77 13:39 ?        00:00:32 /opt/java/bin/java -Dfile.encoding=UTF-8 -Xms256m -Xmx512m -jar /application/halo.jar --spring.r2dbc.url=r2dbc:pool:postgresql://halodb:5432/halo --spring.r2dbc.username=halo --spring.r2dbc.password=q1w2e3r4 --spring.sql.init.platform=postgresql --halo.external-url=http://127.0.0.1:8131/ --server.port=8090

七、配置参数说明

Compose 场景下 Halo 的配置通过 command: 数组传入,等价于命令行参数:

参数本文取值说明
spring.r2dbc.urlr2dbc:pool:postgresql://halodb:5432/halo主机名用 Compose 服务名 halodb,不要写 localhost
spring.r2dbc.usernamehalo与 POSTGRES_USER 一致
spring.r2dbc.passwordq1w2e3r4与 POSTGRES_PASSWORD 一致
spring.sql.init.platformpostgresql必须是 postgresql/mysql/mariadb/h2 之一
halo.external-urlhttp://127.0.0.1:8131/对外地址,影响邮件链接与永久链接
server.port8090容器内端口,与 ports 的右侧一致

💡 说明:--spring.r2dbc.url 里写 localhost 是最常见的错误——容器内的 localhost 是 Halo 自己,连不到数据库。必须用 Compose 服务名(同一自定义网络内由内置 DNS 解析)。

八、配置文件挂载

如果不想把参数堆在 command 里,可改为挂载 application.yaml:

    volumes:
      - ./halo2:/root/.halo2
# ./halo2/application.yaml
server:
  port: 8090
spring:
  r2dbc:
    url: r2dbc:pool:postgresql://halodb:5432/halo
    username: halo
    password: q1w2e3r4
  sql:
    init:
      mode: always
      platform: postgresql
halo:
  work-dir: /root/.halo2
  external-url: http://127.0.0.1:8131

Halo 默认会读取工作目录下的 application.yaml(官方镜像以 /root/.halo2 为工作目录),无需额外加 --spring.config.additional-location。同一项配置「命令行参数」优先级高于配置文件,两者并存时以 command 为准。

九、等价 docker run

不使用 Compose 时,单容器等价命令如下(实测在 8133 端口跑通):

docker network create --subnet 10.89.62.0/24 halo-net

docker run -d --name halo-2271-run \
  --network halo-net \
  -p 8133:8090 \
  -e "JVM_OPTS=-Xms256m -Xmx512m" \
  -v ~/halo/halo2:/root/.halo2 \
  halohub/halo:2.27.1 \
  --spring.r2dbc.url=r2dbc:pool:postgresql://halodb:5432/halo \
  --spring.r2dbc.username=halo \
  --spring.r2dbc.password=q1w2e3r4 \
  --spring.sql.init.platform=postgresql \
  --halo.external-url=http://127.0.0.1:8133/ \
  --server.port=8090

实测验证:

halo-2271-run-check	Up 35 seconds
run8133=302

302 表示控制台已就绪并跳转登录页。注意该命令依赖已存在的 halodb 容器与 halo-net 网络,单独执行前需先按第四章启动数据库服务。

十、常用运维命令

cd ~/halo
docker compose ps                      # 查看状态
docker compose logs -f halo            # 跟踪应用日志
docker compose restart halo            # 只重启应用
docker compose stop                    # 停止全部
docker compose down                    # 停止并删除容器与网络(保留绑定目录)
docker compose pull && docker compose up -d   # 升级镜像

升级前先备份:

docker exec halo-2.27.1-stack-halodb pg_dump -U halo halo > halo-$(date +%F).sql
tar czf halo2-$(date +%F).tar.gz halo2

实测 pg_dump 输出(节选):

--
-- PostgreSQL database dump
--
-- Dumped from database version 17.6

十一、常见问题

11.1 项目名与我写的不一致

halo-2.27.1-stack 被规范化为 halo-2271-stack,点号被移除。查看实际项目名:

docker compose ls
docker network ls | grep halo

11.2 拉取镜像超时 / 卡在 Pulling

docker pull halohub/halo:2.27.1

实测在受限网络下长时间无进展。处理办法:配置镜像加速器,或在可联网机器上 docker save 后 docker load;也可用官方 JAR 自建同版本镜像(本文实测即为此方案)。

11.3 Halo 启动即退出,日志提示连不上数据库

docker compose logs halo | grep -i "connection\|refused" | head

排查顺序:halodb 是否 healthy、URL 主机名是否写成 localhost、密码是否与 POSTGRES_PASSWORD 一致。

docker exec halo-2.27.1-stack-halodb pg_isready -U halo -d halo

实测回显:

/var/run/postgresql:5432 - accepting connections

11.4 Nginx 返回 502

proxy_pass http://halo:8090 依赖同一网络内的服务名解析。确认 nginx 与 halo 在同一网络:

docker inspect halo-2.27.1-stack-nginx \
  --format '{{range $k,$v := .NetworkSettings.Networks}}{{$k}}{{end}}'

实测回显:

halo-2271-stack_halo-net

11.5 上传附件 413

Nginx 默认 client_max_body_size 1m。在 halo.conf 的 server 块加大后 reload:

docker exec halo-2.27.1-stack-nginx nginx -t
docker exec halo-2.27.1-stack-nginx nginx -s reload

实测回显:

nginx: configuration file /etc/nginx/nginx.conf test is successful

十二、参考链接