先说说从H2内嵌库迁移到PostgreSQL的原因:
最大的原因是因为H2 是内嵌式文件数据库,设计初衷就是给本地开发、自测用的,官方明确不建议正式博客、生产环境长期使用,且H2 所有数据就存在单个本地文件里,没有完善的事务日志、崩溃恢复机制。
而我选择PostgreSQL是因为其支持索引优化、并发连接、复杂查询,后期数据再多也能流畅运行。
直接进入正题:
一、先检查现有 Halo 容器的完整配置
执行下面这一条命令,自动过滤出我们需要的端口、数据目录、环境变量所有信息:
查询容器名:docker ps --format "table {{.Names}}\t{{.Image}}\t{{.Ports}}" | grep halo
提取关键配置:docker inspect halo | grep -A 10 -E '"HostPort"|"Source"|"Env":'
[root@ser586654970291 halo]# docker ps --format "table {{.Names}}\t{{.Image}}\t{{.Ports}}" | grep halo
halo registry.fit2cloud.com/halo/halo-pro:2.24 0.0.0.0:8090->8090/tcp, :::8090->8090/tcp
halodb postgres:15.4 0.0.0.0:5432->5432/tcp, :::5432->5432/tcp
[root@ser586654970291 halo]# docker inspect halo | grep -A 10 -E '"HostPort"|"Source"|"Env":'
"HostPort": "8090"
}
]
},
"RestartPolicy": {
"Name": "on-failure",
"MaximumRetryCount": 3
},
"AutoRemove": false,
"VolumeDriver": "",
"VolumesFrom": null,
--
"Source": "/www/wwwroot/halo-new-docker/halo2",
"Destination": "/root/.halo2",
"Mode": "rw",
"RW": true,
"Propagation": "rprivate"
}
],
"Config": {
"Hostname": "8de18420a2cf",
"Domainname": "",
"User": "",
--
"Env": [
"JVM_OPTS=-Xmx256m -Xms256m",
"PATH=/opt/java/openjdk/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin",
"JAVA_HOME=/opt/java/openjdk",
"LANG=en_US.UTF-8",
"LANGUAGE=en_US:en",
"LC_ALL=en_US.UTF-8",
"JAVA_VERSION=jdk-21.0.10+7",
"HALO_WORK_DIR=/root/.halo2",
"SPRING_CONFIG_LOCATION=optional:classpath:/;optional:file:/root/.halo2/",
"TZ=Asia/Shanghai"
--
"HostPort": "8090"
},
{
"HostIp": "::",
"HostPort": "8090"
}
]
},
"HairpinMode": false,
"LinkLocalIPv6Address": "",
"LinkLocalIPv6PrefixLen": 0,
"SecondaryIPAddresses": null,
"SecondaryIPv6Addresses": null,
"EndpointID": "",
"Gateway": "",二、无缝切换操作步骤
1. 最重要的肯定是先备份!
进入 Halo 后台 → 系统 → 备份 → 立即备份 → 下载完整备份包到本地电脑
2. 停止但不要删除原来的 Halo 容器
docker stop halo
生产环境下建议先不要删除,等新服务完全正常运行 24 小时后再删,留作最后的备份
3. 创建专门的目录存放compose配置
mkdir -p /www/wwwroot/halo-docker cd /www/wwwroot/halo-docker
4. 创建docker-compose.yml 文件
镜像、端口、数据目录、重启策略、时区最好全部和现状保持不变,无缝切换,让用户完全感知不到。
创建并编辑yml:vi docker-compose.yml
services:
halo:
container_name: halo
image: registry.fit2cloud.com/halo/halo-pro:2.24
restart: on-failure:3
depends_on:
halodb:
condition: service_healthy
networks:
halo_network:
volumes:
- ./halo2:/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_OPTS=-Xmx256m -Xms256m
command:
- --spring.r2dbc.url=r2dbc:pool:postgresql://halodb/halo
- --spring.r2dbc.username=halo
- --spring.r2dbc.password=halo
- --spring.sql.init.platform=postgresql
- --halo.external-url=http://localhost:8090/
halodb:
container_name: halodb
image: postgres:15.4
restart: on-failure:3
networks:
halo_network:
ports:
- "5432:5432"
volumes:
- ./db:/var/lib/postgresql/data
healthcheck:
test: [ "CMD", "pg_isready" ]
interval: 10s
timeout: 5s
retries: 5
environment:
- POSTGRES_PASSWORD=halo
- POSTGRES_USER=halo
- POSTGRES_DB=halo此配置没有暴露PostgreSQL 端口:
我认为不需要给PostgreSQL添加端口映射,它只需要在内部网络和Halo通信即可,暴露到公网会有安全风险。
5. 启动新的服务栈
docker-compose up -d
6. 查看启动日志
查看PostgreSQL启动日志:docker-compose logs -f halo-postgres
查看Halo启动日志:docker-compose logs -f halo
若看到 Halo 输出Started HaloApplication in X seconds说明启动成功。
7. 恢复数据
访问Halo后台,此时会进入初始化页面:
上传刚才下载的完整备份包
等待恢复完成(根据数据量大小,大概在1-5 分钟)

8. 验证所有功能
用原来的管理员账号密码登录
检查所有文章、页面、评论是否完整
检查所有附件、图片是否正常显示
可以看到数据库也已经从H2内嵌库转为PostgreSQL 15.14。

三、确认无误后的清理工作
等新服务正常运行 24 小时,确认没有任何问题后,再执行以下清理:
删除原来的旧Halo容器:docker rm halo
可选:删除旧的H2数据库文件(释放空间):rm -rf /root/.halo2/db/
四、日常管理命令
以后所有操作都在/www/wwwroot/halo-docker目录下执行:
# 启动所有服务
docker-compose up -d
# 停止所有服务
docker-compose down
# 重启所有服务
docker-compose restart
# 查看Halo日志
docker-compose logs -f halo
# 查看PostgreSQL日志
docker-compose logs -f halo-postgres五、本次 Halo 数据库迁移踩坑记录与关键注意事项
Halo官方至今只对 15.x系列做了最完整的兼容性测试。
宝塔所有版本的PostgreSQL安装路径统一是/www/server/pgsql/。
宝塔宿主机版对比Docker容器版,对于Docker化部署的Halo来说,宿主机版反而更麻烦:需要手动修改配置文件允许Docker网段访问;备份和恢复需要同时操作宝塔和Docker两个地方;升级和迁移都不如容器版方便。
容器之间不能用 127.0.0.1。若把Halo和PostgreSQL都部署在容器里,那么在Halo的数据库连接地址里绝对不能写127.0.0.1,因为那是 Halo容器自己的回环地址,根本访问不到PostgreSQL容器。
不能手动导出导入SQL。按照网上教程用pg_dump和h2dump工具手动导出导入SQL,这是极其危险的做法。H2和PostgreSQL的SQL语法、数据类型、函数都有很多差异,手动转换几乎一定会出现数据丢失或损坏的情况。
新的Halo容器一定要和原来的版本完全一致,不要在迁移的同时升级 Halo 版本,否则可能会出现兼容性问题。
我原来的容器用的是unless-stopped重启策略,比always更合理 —— 如果手动停止了容器,它不会在服务器重启后自动启动。
本文结束。