释放小七猫的分享欲

halo+nginx 搭建博客系统

tags: 软件, NAS
@ 11/09/2025

静态博客与动态博客对比

静态博客系统(如 Hugo)与动态博客系统(如 Halo)在功能、性能和适用场景上存在差异。

场景 静态博客(Hugo) 动态博客(Halo)
个人博客/文档站 ✅ 优先选择(低成本、高性能) ⚠️ 适合需交互功能的场景
企业官网/产品展示 ✅ 快速部署、高安全性 ✅ 需动态内容管理时适用
高并发场景 ✅ 静态资源 CDN 加速优势显著 ❌ 性能瓶颈明显
非技术用户运营 ❌ 依赖技术知识 ✅ 可视化后台降低操作门槛
长期维护需求 ✅ 无需频繁更新环境 ⚠️ 需定期维护服务器和数据库

选择将 Hugo 更换为 Halo 的主要原因是,静态页面难以集成各类 AI 插件,不利于网站的现代化。评论功能并非必需。

迁移思路

Hugo 的文章都放置在 content/post/ 下面,直接备份整个 post 文件夹即可,Halo 可以将 markdown 文章直接导入数据库。

即使更换了博客系统,我们依然可以保留原来的写作方式,即在 post 文件夹中编写文章,然后将文章导入 Halo 中进行发布。这种方式为未来可能的迁移需求提供了便利,避免了大费周章的重复工作。

在 post 文件夹初始化 git,方便文章管理,方法与使用 Hugo 的时候类似。

Halo 搭建并添加 SSL 证书

安装 docker 和 docker-compose

Ubuntu 25.04 安装:

sudo apt install docker.io docker-compose-v2

其他发行版也是类似的。注意,有些发行版的 docker-compose 是 1.29 版本,建议安装 v2 以上版本。

运行 Halo

创建一个文件夹,用于存放 Halo 的所有数据——数据库等,此文档以 ./halo 为例。

Halo 官方提供了多种场景的 Docker Compose 配置文件,主要是数据库的不同,可选:PostgreSQL、MySQL、H2 等。也可以手动配置外部数据库,如果对性能没有特别要求的话,不建议配置外部数据库,管理会麻烦很多。

创建 docker-compose.yml 文件:

version: "3"

services:
    halo:
        image: registry.fit2cloud.com/halo/halo:2 # 最新版 halo
        restart: always # 默认开机自启动
...
volumes:
    - ./data:/root/.halo2 # 映射数据目录
ports:
    - "8090:8090" # http 端口
...
halodb:
    image: postgres:15.4
    restart: always # 默认开机自启动
...
networks:
    halo_network:

先给出几个相比官方 docker-compose 文件需要修改的关键点,后续还会做一些修改。

运行 Nginx

Nginx 的主要作用是给 Halo 添加反向代理和 SSL 证书,SSL 证书需要自行申请,推荐申请 ZeroSSL,不再详述。

Nginx 有两种安装方法:

  1. 直接写在该docker-compose.yml中,仅为 Halo 使用,实现单一功能。
  2. 利用系统现有的 Nginx,添加配置文件实现反向代理。

这里讲解第 1 种方法实现一键配置及设备迁移,对于熟悉 Nginx 的用户,添加反向代理并非难题。

编写一份 nginx.conf,路径为 ./nginx/nginx.conf

# 定义事件处理模块配置
events {
    # 设置每个工作进程的最大连接数
    worker_connections 1024;
}

# 定义 HTTP 模块配置
http {

    # 将 nginx 代理的所有请求实体的大小限制为 1024m
    client_max_body_size 1024m;

    # 配置主 HTTPS 服务器
    server {
        # 监听 IPv4 地址的 8091 端口,启用 SSL
        listen 8091 ssl;
        # 监听 IPv6 地址的 8091 端口,启用 SSL
        listen [::]:8091 ssl;

        # 设置服务器名(可以是域名或 IP)
        server_name localhost;

        # SSL 证书配置
        ssl_certificate /etc/nginx/ssl/ssl.crt;        # SSL 证书文件路径
        ssl_certificate_key /etc/nginx/ssl/ssl.key;    # SSL 私钥文件路径

        # 启用的 SSL/TLS 协议版本
        ssl_protocols TLSv1.2 TLSv1.3;

        # 允许的加密套件,HIGH 表示高强度加密,!aNULL 禁用无认证加密,!MD5 禁用弱 MD5
        ssl_ciphers HIGH:!aNULL:!MD5;

        # 配置反向代理规则
        location / {
            # 将所有请求转发到 Halo 服务的 8090 端口
            proxy_pass http://halo:8090;

            # 设置代理请求头,将客户端真实信息传递给后端
            proxy_set_header Host $host;                # 传递客户端请求的原始 Host 头
            proxy_set_header X-Real-IP $remote_addr;    # 传递客户端真实 IP
            proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; # 添加客户端 IP 到 X-Forwarded-For 头
            proxy_set_header X-Forwarded-Proto $scheme; # 传递原始协议(http/https)
        }
    }
}

核心功能总结:

  1. HTTPS 加密:通过 SSL/TLS 保护数据传输安全。
  2. 反向代理:将外部请求无缝转发到内网 Halo 服务。
  3. 安全优化:限制加密协议版本,禁用弱加密算法。
  4. 大文件支持:允许上传最大 1GB 的文件,防止博客中的图片传不上去。
  5. 注意:暂无 HTTP 重定向功能,需要自行添加。

将 Nginx 集成到 docker-compose.yml

最终的 docker-compose.yml 文件内容

version: "3"

services:
    halo:
        image: registry.fit2cloud.com/halo/halo:2.21.6
        restart: always
        depends_on:
            halodb:
                condition: service_healthy
        networks:
            halo_network:
        volumes:
            - ./data:/root/.halo2
        # 反向代理后无需映射此端口
        # ports:
        #   - "8090:8090"
        healthcheck:
            test:
                [
                    "CMD",
                    "curl",
                    "-f",
                    "http://localhost:8090/actuator/health/readiness",
                ]
            interval: 30s
            timeout: 5s
            retries: 5
            start_period: 30s
        environment:
            # JVM 参数,默认为 -Xmx256m -Xms256m,可以根据实际情况做调整,置空表示不添加 JVM 参数
            - JVM_OPTS=-Xmx256m -Xms256m
        command:
            - --spring.r2dbc.url=r2dbc:pool:postgresql://halodb/halo
            - --spring.r2dbc.username=halo
            # PostgreSQL 的密码,请保证与下方 POSTGRES_PASSWORD 的变量值一致。
            - --spring.r2dbc.password=passwd
            - --spring.sql.init.platform=postgresql
            # 外部访问地址,请根据实际需要修改
            - --halo.external-url=https://your_domain:8091
    halodb:
        image: postgres:15.4
        restart: always
        networks:
            halo_network:
        volumes:
            - ./db:/var/lib/postgresql/data
        healthcheck:
            test: ["CMD", "pg_isready"]
            interval: 10s
            timeout: 5s
            retries: 5
        environment:
            - POSTGRES_PASSWORD=passwd
            - POSTGRES_USER=halo
            - POSTGRES_DB=halo
            - PGUSER=halo

    nginx: # 内置一个反向代理服务器
        image: nginx:alpine
        ports:
            - "8091:8091" # 使用 8091 作为 https 访问端口
        volumes:
            - ./nginx/nginx.conf:/etc/nginx/nginx.conf
            - ./nginx/cert.crt:/etc/nginx/ssl/ssl.crt
            - ./nginx/cert.key:/etc/nginx/ssl/ssl.key
        depends_on:
            - halo
        networks:
            - halo_network

networks:
    halo_network:

需要按需修改的地方:

  1. PostgreSQL 的密码:一定要使用强密码
  2. 外部访问地址:实际的访问域名和端口
  3. https 访问端口:公网 IP 使用 443 即可,家庭宽带由于 443 端口被禁,需要改为别的,实例为 8091
  4. 证书:申请的证书修改为 cert.crt/cert.key,放置到 ./nginx 目录中

启动 docker-compose 项目

如果操作正确,目前的目录结构应该是这样的:

注意证书的路径一定要正确映射。

使用 docker-compose up -d 启动项目,不出意外的话,等一会就可以通过 https://your_domain:port 来安全访问网站了。

文章迁移

图片管理

图片的名称不要重复,名称重复的图片(哪怕在不同的路径下)在上传后会被自动添加后缀,不利于管理,在迁移之前要先把文章中的图片名称检查一遍,该修改的修改,该调整的调整。

插件:内容助手

只能一篇一篇地迁移,不能批量,迁移一篇文章结束后,一定要把图片和文章都清除,再迁移下一篇文章,否则会导致文章重复上传。如果重复上传了也没关系,可以在附件中进行删除。

经过研究,可以批量迁移,但必须保证每篇文章的文件名不同,hugo 一般全部用的 index.md,因此需要逐文件重命名文档,推荐使用 AI 完成。确保文档名和图片都无重复后,可以直接上传整个文件夹,就不用一篇篇上传了。

迁移后的文章,“标题”markdown 文件内的 # 标题,别名为 markdown 文件名,发表时间为现在的时间,需要进行修改。

摘要留空即可。

插件:highlight.js 代码高亮

Halo 中的文章原生没有提供代码高亮的支持,因此需要手动添加功能插件,推荐使用 highlight.js。

任何主题都可以使用。

插件:自动备份插件

支持按天/小时进行自动备份、自动删除冗余备份。

总结

本文详细介绍了如何使用 Halo 和 Nginx 搭建一个功能完善的博客系统,并提供了从 Hugo 迁移的思路和具体步骤。通过 docker-compose 一键部署,简化了环境配置的复杂度,同时通过 Nginx 实现了 SSL 证书的添加和反向代理,保障了网站的安全性。文章迁移部分则重点讲解了图片管理和内容助手插件的使用,帮助用户顺利完成博客内容的迁移。