本手册旨在帮助管理员理解并快速部署基于 Docker Compose 的 Halo 博客系统。该配置针对 24G 内存服务器 进行了性能优化,并采用了环境变量解耦的安全策略。


🏗️ 1. 核心架构说明

该配置采用典型的 App-DB(应用 - 数据库)分层架构

  • Halo 容器 (halo_app):博客主程序,处理业务逻辑、渲染网页。

  • PostgreSQL 容器 (halo_db_server):关系型数据库,存储文章内容、设置、用户信息。

  • 虚拟网桥 (halo_internal_net):创建一个受保护的局域网,使两个容器可以通过名称(如 halodb)互相访问,而不受外部网络干扰。


🛠️ 2. 环境准备(部署前必做)

在宝塔面板或 SSH 终端执行以下步骤,确保基础环境就绪:

① 创建目录并授权

由于 Docker 以 root 权限运行,但内部程序(如 Postgres)有特定的用户 UID,建议预先创建目录并开放读写权限:

# 创建存储数据的根目录
mkdir -p /www/wwwroot/halo/data /www/wwwroot/halo/db

# 赋予最高权限以防止数据库启动失败 (Permission Denied)
chmod -R 777 /www/wwwroot/halo/

② 防火墙放行

在宝塔【安全】或云服务器【安全组】中开放以下端口:

  • 8090:博客访问入口。

  • 5432:数据库管理入口(若不需要远程连接数据库,可不开放)。


⚙️ 3. 关键配置项深度解析

🔋 性能调优:JVM 内存

- JVM_OPTS=-Xmx2048m -Xms512m
  • Xmx (最大堆内存):2G。在 24G 内存的服务器上,这能确保博客在高并发、处理大量图片插件时依然丝滑,且不会造成系统内存拥堵。

  • Xms (初始内存):512M。启动时即分配,减少初始运行时的内存申请开销。

🛡️ 自愈机制:健康检查 (Healthcheck)

healthcheck:
  test: ["CMD", "curl", "-f", "http://localhost:8090/..."]
  start_period: 60s
  • 逻辑:Docker 每 30 秒“问”一次 Halo 是否活着。

  • start_period:因为 Java 程序(Spring Boot)冷启动较慢,如果前 60 秒内报错,Docker 会忽略,不会误以为程序崩溃而不断重启。

🌐 域名依赖:External URL

- HALO_EXTERNAL_URL=http://localhost:8090/
  • 重要性这是小白最容易出错的地方。Halo 后台登录需要校验这个地址。如果你绑定了域名(如 https://blog.com),请务必在此处同步修改。


🚀 4. 部署与维护手册

启动命令

/www/wwwroot/halo 目录下运行:

docker-compose up -d

状态检查

# 查看容器是否都在运行
docker ps

# 实时查看启动日志(排查报错神器)
docker logs -f halo

如何修改数据库密码?

  1. 停止容器:docker-compose down

  2. 修改 YAML 中 两个服务 的密码项(必须保持一致)。

  3. 注意:如果数据库已经初始化,直接改配置可能无效,建议在首次部署前就确定好强密码。


❓ 5. 常见问题 (FAQ)

Q: 为什么状态显示 StartingUnhealthy

A: Java 应用启动通常需要 30-60 秒。请使用 docker logs -f halo 查看日志。如果一直卡住,请检查是否为 /www/wwwroot/halo/db 权限不足导致数据库崩溃。

Q: 我可以用 Navicat 连接数据库吗?

A: 可以。使用服务器公网 IP,端口 5432,用户名 halo,密码使用你在配置中设置的值。

Q: 如何更新 Halo 版本?

A: 修改 image 标签中的版本号(如 2.24.1 改为 2.25.0),然后重新运行 docker-compose up -d,Docker 会自动拉取新镜像并平滑更新。如果要保持2.x 最新版,版本号改为2即可。


🌰 6. 完整版配置示例

# ==============================================================================
# 🚀 HALO 2.X 博客系统 - 自动化部署母版 (2026 生产级标准)
# ==============================================================================
# 💡 使用说明:
# 1. 本文件已移除所有隐私(路径、密码、IP),直接复制即可安全分享。
# 2. 采用了 ${变量:-默认值} 格式,即使不配置外部变量也能按默认路径运行。
# 3. 适配宝塔面板、大内存服务器(如 Oracle Cloud 24G 实例)。
# ==============================================================================

services:
  # ----------------------------------------------------------------------------
  # [模块 A] Halo 博客主程序
  # ----------------------------------------------------------------------------
  halo:
    # 镜像地址:官方推荐的稳定版,建议锁定版本号以防自动更新导致数据不兼容
    image: registry.fit2cloud.com/halo/halo:2.24.1
    container_name: halo_app
    restart: always                                 # 策略:服务器重启或程序崩溃时,自动尝试无限次重启
    
    # 启动顺序:必须等下方的 [halodb] 数据库通过健康检查后,博客程序才开始引导
    depends_on:
      halodb:
        condition: service_healthy 

    networks:
      - halo_internal_net                           # 网络:加入内部隔离网络,不与外部无关容器混用

    volumes:
      # 【持久化挂载】:[服务器实际路径] : [容器内部路径]
      # 作用:把你上传的图片、安装的主题、插件存放在服务器硬盘上,容器删了数据还在
      - ${BASE_PATH:-/www/wwwroot/halo}/data:/root/.halo2

    ports:
      # 【端口映射】:[服务器对外端口] : [容器内部端口]
      # 作用:让你通过 http://IP:8090 能够访问到博客
      - "${WEB_PORT:-8090}:8090"

    healthcheck:
      # 【自愈检查】:每 30 秒探测一次后台接口,确保 Java 程序没有假死
      test: ["CMD", "curl", "-f", "http://localhost:8090/actuator/health/readiness"]
      interval: 30s                                 # 检查频率
      timeout: 5s                                   # 响应超时
      retries: 5                                    # 连续失败 5 次则标记为“不健康”并尝试重启
      start_period: 60s                             # 启动缓冲:Java 启动较慢,前 1 分钟不进行强制判定

    environment:
      # 【JVM 调优】:针对 24G 内存机器,分配 2G 运行内存给博客,保证极致流畅
      - JVM_OPTS=-Xmx2048m -Xms512m
      
      # --- 数据库底层连接环境变量 (强制覆盖模式,比 command 更稳) ---
      # 格式:协议://数据库容器名:端口/数据库名
      - SPRING_R2DBC_URL=r2dbc:pool:postgresql://halodb:5432/${DB_NAME:-halo_db}
      - SPRING_R2DBC_USERNAME=${DB_USER:-halo_user}
      - SPRING_R2DBC_PASSWORD=${DB_PASS:-your_strong_password} # 【脱敏:此处填写你的数据库密码】
      - SPRING_SQL_INIT_PLATFORM=postgresql
      
      # 【访问域名】:请在此处填写你的真实访问地址(带 http/https),否则后台无法正常登录
      - HALO_EXTERNAL_URL=${SITE_URL:-http://localhost:8090/}
    
    # 💡 关键:清空默认指令,防止干扰上述 SPRING 环境变量的生效
    command: [] 

  # ----------------------------------------------------------------------------
  # [模块 B] PostgreSQL 数据库引擎
  # ----------------------------------------------------------------------------
  halodb:
    # 镜像地址:使用 alpine 版,不仅体积小(仅约 1/4),且安全性更高
    image: postgres:15-alpine
    container_name: halo_db_server
    restart: always

    networks:
      - halo_internal_net                           # 数据库仅在内部网桥运行,确保公网无法通过容器名攻击

    volumes:
      # 【持久化挂载】:存储所有的文章文字、用户信息、设置项
      - ${BASE_PATH:-/www/wwwroot/halo}/db:/var/lib/postgresql/data

    ports:
      # 【数据库映射】:方便你用 Navicat 或 DBeaver 在电脑本地远程管理数据库
      # 💡 安全建议:如果不需要远程连接,可以将这一行删掉或在防火墙限制 IP
      - "${DB_PORT:-5432}:5432"

    healthcheck:
      # 【就绪判定】:使用官方工具 pg_isready 确保数据库不仅在运行,且可以接受新连接
      test: [ "CMD-SHELL", "pg_isready -U ${DB_USER:-halo_user} -d ${DB_NAME:-halo_db}" ]
      interval: 10s
      timeout: 5s
      retries: 5

    environment:
      # 数据库初始化参数:这些参数必须与上面 [halo] 模块中的配置一一对应
      - POSTGRES_PASSWORD=${DB_PASS:-your_strong_password} # 数据库超级密钥
      - POSTGRES_USER=${DB_USER:-halo_user}               # 数据库登录用户名
      - POSTGRES_DB=${DB_NAME:-halo_db}                   # 默认创建的数据库库名
      - PGUSER=${DB_USER:-halo_user}                      # 指定健康检查所用的用户

# ------------------------------------------------------------------------------
# [模块 C] 网络隔离定义
# ------------------------------------------------------------------------------
networks:
  # 创建一个名为 halo_internal_net 的独立虚拟局域网
  halo_internal_net:
    driver: bridge                                  # 驱动:桥接模式,提供容器间 DNS 自动发现功能