从零搭建 Halo 博客:Docker + Caddy + PostgreSQL 完整指南
前言
本文记录了从零在 VPS 上搭建 Halo 博客的完整过程。技术栈:
- Halo 2.24.1 — 开源博客系统
- Docker + Docker Compose — 容器化部署
- Caddy — 反向代理,自动申请 HTTPS 证书(注意:不是 Nginx)
- PostgreSQL 16 — 生产级数据库
一、环境要求
- 一台 VPS(本文使用 Oracle Cloud Ubuntu 22.04)
- 已解析到 VPS 的域名(通过 Cloudflare 关联)
- SSH 访问权限
二、安装 Docker
# 一键安装 Docker
curl -fsSL https://get.docker.com | sudo sh
# 将当前用户加入 docker 组(之后免 sudo)
sudo usermod -aG docker $USER
重新登录 SSH 后生效。
三、目录结构
所有文件统一放在 ~/halo-blog/ 下:
~/halo-blog/
├── docker-compose.yml # 服务编排文件
├── Caddyfile # 反向代理配置
├── halo-data/ # Halo 数据(文章、主题、附件等)
├── pg-data/ # PostgreSQL 数据
├── caddy-data/ # Caddy 证书数据
└── caddy-config/ # Caddy 配置缓存
四、docker-compose.yml 完整配置
services:
db:
image: postgres:16-alpine
container_name: halo-db
restart: unless-stopped
volumes:
- ./pg-data:/var/lib/postgresql/data
environment:
- POSTGRES_DB=halo
- POSTGRES_USER=halo
- POSTGRES_PASSWORD=your_strong_password
networks:
- halo-net
healthcheck:
test: ["CMD-SHELL", "pg_isready -U halo -d halo"]
interval: 10s
timeout: 5s
retries: 5
halo:
image: halohub/halo:2.24.1
container_name: halo
restart: unless-stopped
depends_on:
db:
condition: service_healthy
volumes:
- ./halo-data:/root/.halo2
environment:
- HALO_WORK_DIR=/root/.halo2
- SPRING_R2DBC_URL=r2dbc:pool:postgresql://db:5432/halo
- SPRING_R2DBC_USERNAME=halo
- SPRING_R2DBC_PASSWORD=your_strong_password
- SPRING_SQL_INIT_PLATFORM=postgresql
networks:
- halo-net
caddy:
image: caddy:2-alpine
container_name: caddy
restart: unless-stopped
ports:
- "80:80"
- "443:443"
volumes:
- ./Caddyfile:/etc/caddy/Caddyfile
- ./caddy-data:/data
- ./caddy-config:/config
networks:
- halo-net
networks:
halo-net:
driver: bridge
将
your_strong_password替换为你自己的强密码,两处保持一致。
五、Caddyfile 配置
blog.yourdomain.com {
reverse_proxy halo:8090
encode gzip
}
Caddy 反向代理原理
用户浏览器
│ HTTPS 443
▼
┌─────────────┐
│ Caddy │ ← 监听宿主机 80/443,自动管理 SSL 证书
└──────┬──────┘
│ HTTP 8090(Docker 内部网络)
▼
┌─────────────┐
│ Halo │ ← 只在内部网络,外部无法直接访问
└──────┬──────┘
│
┌─────────────┐
│ PostgreSQL │ ← 只在内部网络,不对外暴露
└─────────────┘
关键点:
1. 只有 Caddy 暴露了 80/443 端口到宿主机
2. Halo 和 PostgreSQL 仅在 Docker 内部网络 halo-net 中可见
3. 容器间通过容器名互相访问(halo、db 相当于内部 DNS)
4. Caddy 会自动向 Let's Encrypt 申请并续期 SSL 证书,无需手动操作
与 Nginx 的区别:Nginx 需要手动配置证书(certbot),Caddy 全自动,配置文件极简。
六、首次启动
cd ~/halo-blog
sudo docker compose up -d
等待约 1 分钟(首次需拉取镜像),然后访问 https://yourdomain.com/system/setup 完成初始化向导,设置管理员账号。
七、Docker 常用管理命令
查看运行状态
cd ~/halo-blog
sudo docker compose ps
查看实时日志
# 所有服务日志
sudo docker compose logs -f
# 只看 Halo
sudo docker logs halo -f
# 只看最近 50 行
sudo docker logs halo --tail=50
进入容器内部
# 进入 Halo 容器(精简镜像用 sh)
sudo docker exec -it halo sh
# 进入 PostgreSQL 并打开数据库终端
sudo docker exec -it halo-db psql -U halo -d halo
# 在 psql 中常用命令
# \dt 查看所有表
# \q 退出
# SELECT name FROM extensions LIMIT 10; 查看数据
查看容器资源占用
sudo docker stats
启停服务
sudo docker compose stop halo # 停止 Halo(不删除容器)
sudo docker compose start halo # 启动 Halo
sudo docker compose restart halo # 重启 Halo
sudo docker compose down # 停止并删除所有容器(数据不会丢失)
sudo docker compose up -d # 重新创建并启动
八、更新 Halo 版本
Halo 支持平滑升级,数据库迁移自动完成。
cd ~/halo-blog
# 第一步:修改 docker-compose.yml 中的版本号
# 将 halohub/halo:2.24.1 改为新版本,例如 halohub/halo:2.25.0
# 第二步:拉取新镜像
sudo docker compose pull halo
# 第三步:重新启动
sudo docker compose up -d halo
# 验证版本
sudo docker logs halo 2>&1 | grep "Version:"
建议:更新前先备份数据(见下节)。
九、数据备份
方法一:Halo 后台一键备份(推荐)
登录 https://yourdomain.com/console/ → 系统 → 备份 → 点击「创建备份」。
备份为 zip 文件,包含所有文章、附件、主题设置,可直接用于恢复或迁移。
方法二:手动备份
# 停止 Halo(避免数据不一致)
sudo docker compose stop halo
# 备份 Halo 文件数据(主题、附件等)
tar -czf ~/halo-data-$(date +%Y%m%d).tar.gz -C ~/halo-blog halo-data/
# 备份 PostgreSQL 数据库
sudo docker exec halo-db pg_dump -U halo halo > ~/halo-db-$(date +%Y%m%d).sql
# 重新启动
sudo docker compose start halo
方法三:定时自动备份
# 创建备份目录
mkdir -p ~/backups
# 编辑 crontab
crontab -e
# 添加:每天凌晨 3 点自动备份
0 3 * * * cd ~/halo-blog && sudo docker exec halo-db pg_dump -U halo halo > ~/backups/db-$(date +\%Y\%m\%d).sql && tar -czf ~/backups/data-$(date +\%Y\%m\%d).tar.gz -C ~/halo-blog halo-data/ 2>/dev/null
# 查看 crontab 是否生效
crontab -l
十、迁移到新 VPS
所有数据都在 ~/halo-blog/ 目录中,迁移非常简单。
步骤一:旧 VPS 打包数据
cd ~/halo-blog
# 停止所有服务
sudo docker compose down
# 打包整个目录
tar -czf ~/halo-blog-migration.tar.gz -C ~/ halo-blog/
步骤二:传输到新 VPS
# 在本地机器执行
scp user@old-vps:~/halo-blog-migration.tar.gz user@new-vps:~/
# 或使用 rsync(支持断点续传)
rsync -avz --progress user@old-vps:~/halo-blog/ user@new-vps:~/halo-blog/
步骤三:新 VPS 恢复
# 安装 Docker
curl -fsSL https://get.docker.com | sudo sh
sudo usermod -aG docker $USER
# 解压数据
tar -xzf ~/halo-blog-migration.tar.gz -C ~/
# 将域名 DNS 解析指向新 VPS IP
# (Cloudflare 中修改 A 记录)
# 启动服务
cd ~/halo-blog
sudo docker compose up -d
等待约 1 分钟后访问博客,所有数据完整保留,Caddy 会自动为新 IP 重新申请证书。
提示:如果域名不变,也可以直接复制
caddy-data/目录,省去重新申请证书的等待时间。
十一、常用操作速查表
| 操作 | 命令 |
|---|---|
| 启动全部服务 | sudo docker compose up -d |
| 停止全部服务 | sudo docker compose down |
| 查看运行状态 | sudo docker compose ps |
| 查看 Halo 日志 | sudo docker logs halo -f |
| 进入 Halo 容器 | sudo docker exec -it halo sh |
| 进入数据库 | sudo docker exec -it halo-db psql -U halo -d halo |
| 更新版本 | 改版本号 → pull → up -d |
| 手动备份 DB | sudo docker exec halo-db pg_dump -U halo halo > backup.sql |
| 迁移博客 | 打包 halo-blog/ → 新机器解压 → up -d |
总结
这套架构的核心优势是容器化部署,所有数据文件化。无论备份、恢复还是迁移,都只需要操作文件目录,没有复杂的环境依赖。整个博客只需要 Docker 就能完整运行。
许可协议:
CC BY 4.0