如果是部署在国外服务器上, 则不推荐使用 Halo
摘要: 本文手把手教你在轻量级服务器(1G 内存)上,利用 Docker 部署 Halo 2.0,配置 15 年有效期的 SSL 证书,并实现 Cloudflare R2 异地自动备份。低成本也能拥有高可用!
大家好!今天分享一下我是如何搭建个人博客的。
基于这些需求,我总结了这套 Halo 2.0 + Docker + Cloudflare (全家桶) 的最佳实践方案。
我的目标很明确:成本要低(服务器配置不高),数据要稳(必须有备份),维护要少(证书自动续期)。

一、 给服务器穿上“防爆服” (配置 Swap)
我的服务器内存只有 1G。Java 应用(Halo)加上数据库,启动时很容易因为内存波动导致 OOM(内存溢出)被系统杀掉。
因此,第一步必须配置 Swap (虚拟内存)。如果你直接按照官方教程部署,很可能会经常遇到博客无故宕机的情况。
操作步骤:
连接到服务器终端,执行以下脚本。这个脚本会自动检测是否存在旧的 Swap,并创建一个新的 2G Swap 文件,同时优化内核参数(让系统尽量优先用物理内存)。
Bash
#!/bin/bash
# 1. 检查并清理旧 Swap
if [ -f /swapfile ]; then
echo "发现旧 Swap,正在卸载..."
sudo swapoff /swapfile
sudo rm /swapfile
fi
# 2. 创建 2G Swap
echo "正在创建 2G Swap..."
sudo fallocate -l 2G /swapfile
sudo chmod 600 /swapfile
sudo mkswap /swapfile
sudo swapon /swapfile
# 3. 写入 fstab 实现开机自启 (防止重启失效)
# 先清理旧配置防止重复
sudo sed -i '/\/swapfile/d' /etc/fstab
echo '/swapfile none swap sw 0 0' | sudo tee -a /etc/fstab
# 4. 优化 Swappiness (值越低越倾向于使用物理内存)
sudo sysctl vm.swappiness=10
echo 'vm.swappiness=10' | sudo tee -a /etc/sysctl.conf
echo "✅ Swap 配置完成!"执行完毕后,输入 free -h 查看结果:

(👆 图注:如图所示,Swap 一栏显示 2.0Gi,说明服务器有了“备用血条”。)
- *
二、 使用 Docker Compose 极速部署
相比于手动安装 Java 环境,Docker 更加干净卫生,升级也只需要拉取新镜像即可。
1. 准备目录
Bash
mkdir -p ~/halo && cd ~/halo2. 编写配置文件
创建 docker-compose.yaml。这里有两个关键点:
- 显式限制了 JVM 的最大堆内存 (
-Xmx512m),防止 Java 吃光资源。 - 使用 PostgreSQL 数据库,比 H2 更稳定。
YAML
services:
halo:
image: halohub/halo:2.20 # 建议锁定具体版本号
container_name: halo
restart: always
depends_on:
halodb:
condition: service_healthy
networks:
- halo_network
volumes:
- ./halo-data:/root/.halo2
ports:
- "8090:8090"
command:
# 数据库配置
- --spring.r2dbc.url=r2dbc:pool:postgresql://halodb/halo
- --spring.r2dbc.username=halo
- --spring.r2dbc.password=YourStrongPassword! # ⚠️ 改成你的强密码
- --spring.sql.init.platform=postgresql
# 外部访问地址 (换成你的域名)
- --halo.external-url=https://www.diggingfly.com
# JVM 内存限制 (关键!)
- -Xms256m -Xmx512m
halodb:
image: postgres:15-alpine
container_name: halodb
restart: always
networks:
- halo_network
volumes:
- ./db-data:/var/lib/postgresql/data
environment:
- POSTGRES_DB=halo
- POSTGRES_USER=halo
- POSTGRES_PASSWORD=YourStrongPassword! # ⚠️ 必须与上方一致
healthcheck:
test: ["CMD", "pg_isready", "-U", "halo"]
interval: 10s
timeout: 5s
retries: 5
networks:
halo_network:3. 启动与初始化
执行命令启动:
Bash
docker compose up -d查看容器状态:
docker compose ps
(👆 图注:当 Status 显示为 (healthy) 时,说明服务启动成功。)
在浏览器访问 http://服务器IP:8090,即可看到初始化界面:

三、 SSL 证书:Let's Encrypt 自动化部署 (acme.sh)
使用 acme.sh 脚本,配合 Cloudflare DNS API 申请证书。 优势: 泛域名支持、不需要关闭 Nginx 即可验证、完全自动续期(无需手动介入)。
1. 获取 Cloudflare API Token
登录 Cloudflare -> 点击管理帐户 -> 帐户 API 令牌 -> 创建令牌。

点击 Create Token -> 选择模板 Edit zone DNS。

区域资源 选择 包括 -> 特定区域 -> 选择你的域名 (diggingfly.com)。

点击 Continue,复制生成的 API Token。

(👆 图注:复制这段长长的 Token,这是脚本操作 DNS 的钥匙。)
2. 安装 acme.sh 并配置
在服务器终端执行:
Bash
# 1. 安装 acme.sh (邮箱换成你自己的,用于接收过期提醒)
curl https://get.acme.sh | sh -s [email protected]
# 2. 使命令生效
source ~/.bashrc
# 3. 导入 Cloudflare Token 环境变量
# 注意:export 命令在重启后会失效,但 acme.sh 会把这些凭证自动保存到它的配置文件中,所以只需执行一次
export CF_Token="你刚才复制的API_Token"
export CF_Account_ID="你的Cloudflare_Account_ID"
# (Account ID 在 Cloudflare 域名概览页右下角可以找到)3. 申请证书 (DNS 模式)
执行下面这行命令,脚本会自动向 Cloudflare 添加 TXT 记录进行验证,验证完后会自动删除。
Bash
# 申请泛域名证书 (*.diggingfly.com) 和主域名证书
acme.sh --issue --dns dns_cf -d diggingfly.com -d "*.diggingfly.com"如果看到绿色的 Cert success,说明申请成功!
4. 安装证书到 Nginx 目录
注意: 不要直接引用 .acme.sh 目录下的文件。应该使用 --install-cert 命令把证书“复制”到 Nginx 目录,这样脚本才能记住“续期后要更新这里”。
Bash
# 1. 创建存放目录
sudo mkdir -p /etc/nginx/cert
# 2. 安装证书并指定重载命令
acme.sh --install-cert -d diggingfly.com \
--key-file /etc/nginx/cert/key.pem \
--fullchain-file /etc/nginx/cert/cert.pem \
--reloadcmd "sudo nginx -s reload"✅ 自动续期机制: acme.sh 安装时已经自动向系统添加了一个 crontab 定时任务。它每天会自动检测证书是否快过期,如果快过期了,它会自动:
- 调用 Cloudflare API 重新验证。
- 更新
/etc/nginx/cert/下的文件。 - 执行
nginx -s reload重启服务。 全程无需你操心。
四、 Nginx 反向代理配置
我们需要 Nginx 监听 80 和 443 端口,并将流量转发给 Halo 的 8090 端口。
同时,这里解决了**“上传大图片失败”**的问题(Nginx 默认只允许 1M)。
编辑 /etc/nginx/conf.d/halo.conf:
Nginx
# HTTP -> HTTPS 重定向
server {
listen 80;
server_name www.diggingfly.com diggingfly.com;
# 301 永久重定向
return 301 https://$host$request_uri;
}
# HTTPS 主配置
server {
listen 443 ssl http2;
server_name www.diggingfly.com diggingfly.com;
# 1. 证书路径 (指向 acme.sh 安装的位置)
ssl_certificate /etc/nginx/cert/cert.pem;
ssl_certificate_key /etc/nginx/cert/key.pem;
# 2. SSL 安全参数 (推荐配置)
ssl_protocols TLSv1.2 TLSv1.3;
ssl_ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384;
ssl_prefer_server_ciphers off;
# HSTS (可选,强制浏览器记住 HTTPS,提高安全性)
add_header Strict-Transport-Security "max-age=63072000" always;
# 3. 核心优化:解除上传限制
client_max_body_size 100m;
# 4. Gzip 压缩
gzip on;
gzip_min_length 1k;
gzip_types text/plain application/javascript text/css application/xml;
# 5. 反向代理到 Halo
location / {
proxy_pass http://127.0.0.1:8090;
# 传递真实 IP
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;
# 如果你用了 Cloudflare CDN,建议加上这一行,把 CF 的真实 IP 传给 Halo
# proxy_set_header X-Real-IP $http_cf_connecting_ip;
# WebSocket 支持
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
}
}配置完成后,重启 Nginx:
Bash
sudo nginx -t
sudo nginx -s reload现在访问你的域名,你应该能看到那把安全的“小锁”了!🔒
五、 图床配置:Cloudflare R2 免费使用
数据只放在服务器本地是不安全的。我使用了 Cloudflare R2 对象存储,它兼容 AWS S3 协议,且拥有 10GB 的免费存储额度,对于用做博客图床来说绰绰有余。
1. 创建 R2 存储桶
在 Cloudflare 后台 -> R2,创建一个 Bucket,命名为 bolg-img。
进入 Bucket 详情页,复制 Endpoint 地址。

2. 创建 API 令牌
在 R2 页面右侧点击 Manage R2 API Tokens -> Create API Token。

权限选择:Object Read & Write, 并指定 Bucket:blog-img (为了安全,只授权这一个), 其它的默认即可

创建后,保存好 Access Key ID 和 Secret Access Key。

3. Halo 插件配置
进入 Halo 后台,安装 S3 插件。

进入插件菜单启用 S3 插件

在 设置 -> 存储策略 中添加 S3:

- Access Key / Secret Key: 填入刚才获取的令牌。
- Endpoint: 填入 R2 的地址(注意:去掉链接末尾的 bucket 名字,只保留域名)。
- Region: 填
auto。

(👆 图注:这是最容易填错的一步,请仔细核对 Endpoint 格式)
4. 启用文章图片自动上传到 S3
在设置 -> 文章设置 -> 附件存储策略 里选择你刚刚创建的存储策略, 然后保存

这配置在2.22.0中已经废弃了
在设置 -> 附件设置 -> 管理端附件配置 里选择你刚刚创建的存储策略, 然后保存

2.22.0 版本后需要在此配置
六. 自动备份
创建一个自动备份策略,选择每天凌晨 3 点自动备份。
1. 安装备份插件
在应用市场里搜索 “备份” , 安装自动备份插件
2. 启用 备份插件
在插件中启用 “自动备份插件”

3. 自动备份
点击 ‘自动备份插件’ , 进入插件配置页, 点击基本设置配置备份策略

七、换个新皮肤:主题安装与配置 (以 Clarity 为例)
Halo 默认的主题虽然简洁,但可能不够个性。Halo 2.0 拥有强大的应用市场,换主题就像给手机换壁纸一样简单。 这里我们以 Clarity 为例,这是一款非常适合个人开发者的“简历/名片风”主题,简洁且能突出个人介绍。
1. 在应用市场下载主题
- 进入 Halo 后台,点击左侧菜单的 应用市场 (App Store)。
- 在左侧分类选择 主题 (Themes)。
- 在搜索框输入 Clarity,找到 Clarity。
- 点击 安装 (Install)。

Halo 的生态越来越丰富,一键安装省去了上传文件的麻烦。
2. 启用主题
下载完成后,主题并不会自动切换,需要手动启用。
- 点击左侧菜单的 外观 (Appearance) -> 主题 (Themes)。
- 你会看到刚才下载的 vCard4 已经出现在列表里了。
- 点击主题卡片下方的 启用 (Enable) 按钮。

点击启用后,进入博客首页查看变化
3. 配置主题 (可选)
主题更精细的配置可以到 主题 菜单下配置
- 配置主导航菜单: 这个默认不显示, 你得在 侧边栏头部 -> 主导航菜单,将 主菜单 勾上。

5. 预览效果
现在,打开浏览器访问你的域名 https://www.diggingfly.com 查看预览效果

大功告成!这是属于你在互联网上的第一张名片
八、 开启上帝视角:接入 Google Analytics 4
博客建好了,自然想知道有没有人看。我们来接入业界最标准的统计工具——Google Analytics (GA4)。
1. 获取追踪代码 (Gtag)
访问 analytics.google.com 并登录 Google 账号。

创建媒体资源:
- 名称:
DiggingFly Blog - 时区:选择你所在的地区
- 货币:随意

商家描述:
- 行业类别:
互联网与电信 - 业务规模:
小型- 1 至 10 名员工

业务目标按需选择

选择平台: 点击 "Web (网站)"。

设置数据流:
- 网址:
www.diggingfly.com(注意选 https://) - 流名称:
DiggingFly Website - 点击 "创建数据流"。

创建成功后,会弹出一个 "安装说明" 窗口。点击 "手动安装" 标签,你会看到一段以 <script> 开头的代码。

2. 在 Halo 中注入代码
拿到代码后,我们需要把它放入博客的每一个页面中。Halo 2.0 贴心地提供了全局注入功能,不需要修改主题源码。
- 登录 Halo 后台,.
- 进入 设置 (Settings) -> 代码注入。
- 找到 “全局 head 标签 (Head Code)” 输入框。
- 把刚才从 Google 复制的那段
<script>...</script>代码完整粘贴进去。 - 点击右下角的 保存。

3. 验证是否生效
配置完成后,我们来测试一下。

- 打开你的博客首页(建议用手机或者浏览器的无痕模式打开)。
- 回到 Google Analytics 后台,点击左侧菜单的 "报告" -> "实时"。
- 如果你看到地图上出现了一个蓝点,或者“过去 30 分钟的用户数”显示为 1,恭喜你!连接成功!

- *
评论 (0)