Halo + Docker 私有化部署:极简博客系统搭建与运维指南

发布时间:2026/9/30 3:30:15
Halo + Docker 私有化部署:极简博客系统搭建与运维指南 后台经常收到类似的问题就想找个地方安安静静写点东西不想折腾首页轮播图、会员体系、消息推送这些乱七八糟的功能最好打开编辑器就是码字发布完就能分享数据还得在自己的服务器上躺着——那选什么博客系统我的答案这几年一直没变Halo用 Docker 私有化部署。这个组合我实际跑了一年多期间迁移过一次服务器升级过三个大版本数据一次没丢写作体验也一直很稳定。一句话总结它把“写文章”这件事的复杂度降到了最低把“数据可控”这件事拉到了满分。这篇文章就把我的完整部署过程、踩过的坑、以及日常维护里值得注意的细节一并写出来算是给后来者的一份实操参考。1. 为什么是 Halo Docker这个组合到底赢在哪1.1 Halo 凭什么值得私有化部署先说说 Halo 是什么。它是一个开源的现代博客系统用 Java 写的自带一套简洁的后台管理界面和 Markdown 编辑器。如果你见过 WordPress 后台那种满是仪表盘、小工具、侧边栏的界面再看 Halo会有一种“终于有人把博客后台当写作工具来做”的感觉。它有几个点特别对我的胃口编辑器干净专注新建文章就是一个全屏的 Markdown 编辑区左侧写内容右侧看效果没有多余的按钮和干扰。写长文的时候这个体验很重要。插件和主题够用但不臃肿官方市场里有不少主题默认的几个就很克制插件体系支持评论、统计、图床、搜索之类的常见需求装两三个就够不会像 WordPress 那样一不留神装了几十个插件把网站拖垮。数据完全是自己的文章、页面、评论都存在数据库里图片存在服务器目录里随时可以打包带走。这一点对比那些写作平台几乎是降维打击。部署形态灵活Halo 有 Docker 镜像、云平台部署包、传统 jar 包多种形态但真正适合普通人自己折腾的就是 Docker 这种方式。有人会问Hexo、Hugo 这类静态博客不也很极简吗确实静态博客写起来也很纯粹但问题在于每次写文章要先在本地渲染、然后重新部署手机或者别的电脑上没法随手改评论系统还得额外接第三方服务。Halo 是动态博客浏览器登录后台就能写写完即发电脑手机都行。这种“随时打开就能写”的体验才是它作为写作神器的核心价值。1.2 Docker 在私有化部署里的实际价值Docker 这个东西本质上就是一个“极其方便的软件打包与运行环境”。你可以把 Halo 和它依赖的 Java 运行时、配置文件全部打成一个镜像在任何装了 Docker 的机器上一条命令启动跑起来的效果一模一样。对我这种不喜欢折腾环境的人来说Docker 最大的价值有三点第一是环境隔离。Halo 需要 Java 运行环境版本太新太老都可能出问题。用 Docker 之后Java 环境全部装进容器里宿主机上不需要装任何 Java不用担心和服务器上其他软件的依赖冲突。这个对一台服务器上跑了多个服务的人尤其重要。第二是部署可复制。我的一份docker-compose.yml文件就是整台 Halo 服务器的“配置清单”。换服务器的时候把这份文件和挂载目录拷过去一条docker compose up -d服务就原地复活了。不需要重新回忆当时装了什么、改了什么配置。第三是升级和回滚非常容易。Halo 发新版本了我只需要改一下镜像的版本号重新拉取镜像再重建容器即可万一新版本有问题把版本号改回去再重建一次就回滚了。整个过程中数据目录独立在外面容器怎么折腾都不影响文章数据。1.3 私有化部署到底在解决什么问题我再多说一句“为什么非要私有化”。很多人的博客之旅是从公众号、知乎、语雀、Notion 开始的这些平台写作体验各有千秋但都有一个共同问题内容主权不在你手里。平台改规则、关服务、限制导出你都只能被动接受。还有一类情况是内容根本不能发——你写的第一篇随笔就被平台夹了理由还说不清。私有化部署之后服务器是你租的或家里的数据库在你手上备份在你手上域名解析指向的是自己的服务。写什么、放什么、怎么排版完全自己说了算。这种“全链路掌控”的感觉对于认真写作的人来讲是一种安心感。而且现在的云服务器成本并不高一台 1 核 2G 的入门款就够 Halo 跑得很流畅算下来一天几毛钱比买一杯咖啡都便宜。所以Halo Docker 不是最炫的方案但它是把“简单、可控、够用”这三件事平衡得最好的方案。2. 动手之前的准备一台机器、一个容器环境就够了2.1 硬件与机器选型先泼一盆冷水Halo 是 Java 应用内存占用比静态博客高但也没有高到离谱。我在一台1 核 2G的云服务器上实测Halo 容器常驻内存大概300MB ~ 500MB加上 MySQL 容器如果你像我一样外接数据库的占用整机 2G 内存跑起来没什么压力。如果只有 1G 内存的机器也不是不行但建议把 JVM 参数调小一点后面我会给出具体配置。机器可以选的形态很多云服务器主流选择方便绑定域名、配置 HTTPS缺点是每年要续费。家里的旧笔记本/迷你主机性能普遍过剩用 Docker Desktop 或者直接装 Linux 都行适合追求零成本的人。NAS群晖、威联通这些设备自带 Docker 套件把 Halo 跑在 NAS 上挺好资料还能和内网其他文件打通。软路由部分玩家用软路由顺带跑几个轻量容器Halo 的配置也能塞进去但不太建议毕竟路由器的稳定性和散热还是优先保命要紧。操作系统方面如果你用的是云服务器建议选Debian 11/12或Ubuntu 22.04/24.04。不是说你买的服务器就一定要重装成它们而是这两个系统在 Docker 支持上最省心社区资料也最丰富。CentOS 7 那种老系统不是不能用但内核版本低Docker 新特性支持差我踩过一次坑之后就不再推荐了。2.2 安装 Docker 和 Docker Compose云服务器通常是一台全新的、干净的 Linux 系统装 Docker 的步骤很固定。Debian/Ubuntu 系可以直接用官方源安装curl -fsSL https://get.docker.com | bash -这条命令会自动配置 Docker 官方源并安装 Docker Engine 和 compose 插件。安装完成后先把当前用户加入 docker 组这样就不用每次都敲sudosudo usermod -aG docker $USER改完用户组之后要重新登录服务器或者重新连接 SSH才会生效。验证一下docker version docker compose version如果两条命令都正常输出版本信息说明 Docker 环境已经就绪。这里必须提醒一个国内用户几乎都会遇到的问题直接从默认源拉镜像非常慢甚至超时。解决方案是配置镜像加速器。国内常见的加速地址有阿里云镜像加速、中科大 Docker 加速等不同服务商的规则时不时会变如果你用的加速地址失效了就去查一下当前可用的公共加速地址。配置方法编辑/etc/docker/daemon.json写入类似这样的内容以实际可用地址为准{ registry-mirrors: [https://你的加速地址] }然后重启 Dockersudo systemctl daemon-reload sudo systemctl restart docker提示镜像加速只影响 Docker Hub 镜像的拉取速度不影响容器运行。配好之后拉 Halo 镜像基本就是几十秒的事。2.3 端口、目录与域名的规划Halo 默认监听8080端口。8080 不是标准 Web 端口浏览器访问时得手动加端口号体验不太好。我用的是服务器默认策略把容器内的 8080 映射到宿主机的80端口这样访问http://你的服务器IP就能直接打开博客。如果你的机器 80 端口已经被别的服务占用了可以考虑映射到另一个端口但推荐尽早规划和解决占用问题。数据目录我放在/opt/halo里面再分几个子目录/opt/halo ├── docker-compose.yml # 部署配置文件 ├── .halo # Halo 数据目录 │ ├── application.yaml # Halo 配置文件 │ ├── attachments # 上传的图片等附件 │ ├── plugins # 插件 │ └── themes # 主题域名方面如果你有域名解析一条 A 记录到服务器公网 IP然后在后续扩展里配置 HTTPS 即可如果暂时没有域名直接用 IP 访问也完全没问题。唯一要提醒的是国内云服务器 80/443 端口对外提供服务根据平台规则可能需要完成相关备案手续这个你自己评估一下就行技术实现没有区别。3. 完整部署过程实录从空白环境到文章上线3.1 编写 compose 文件每个参数都解释清楚在/opt/halo下创建一个docker-compose.yml文件。我的最终版本长这样services: halo: image: halohub/halo:2.17 container_name: halo restart: always depends_on: - halo_db ports: - 80:8080 environment: - TZAsia/Shanghai volumes: - ./.halo:/root/.halo2 command: - --spring.r2dbc.urlr2dbc:pool:mysql://halo_db:3306/halo - --spring.r2dbc.usernamehalo - --spring.r2dbc.password你的数据库密码 - --halo.external-urlhttp://你的域名或IP如果你只是个人写写文章不想用 MySQL 那么“重”的数据库Halo 也支持内嵌 H2 数据库。那版本可以简化成services: halo: image: halohub/halo:2.17 container_name: halo restart: always ports: - 80:8080 environment: - TZAsia/Shanghai volumes: - ./.halo:/root/.halo2这里的几个关键点我分别说一下不然以后你想改配置容易懵image官方镜像我建议指定版本号而不是用latest。latest虽然方便但哪天官方推了新版本你随手pull一下再重建容器就把版本升上去了万一有破坏性变更就得临时想办法回滚。锁定一个具体版本号更可控。restart: always服务器重启后容器会自动启动不用手动去敲命令。对没有运维经验的个人站长来说这一行能省很多麻烦。ports: 80:8080宿主机 80 端口映射到容器内 8080 端口。容器内的 Halo 默认监听 8080这是它自己的逻辑端口“80”是宿主机对外提供服务的端口改成 “8080:8080” 也是可以的就看你想不想在访问时带端口号。volumes把宿主机的.halo目录挂载为容器内/root/.halo2。这里路径要注意Halo 2.x 版本数据目录是/root/.halo2不是 1.x 的/root/.halo网上很多老教程写的是后者照搬会踩坑。TZAsia/Shanghai设置时区为上海不然文章发布时间的“今天凌晨”和你的北京时间对不上发布文章的显示时间会差 8 小时。command 里的数据库参数这是给 Halo 启动时注入的配置指示它连接 MySQL。暂时不用 MySQL 的可以跳过这部分。如果你完全按我上面的方式写我记得 Halo 2.x 对 H2 内嵌数据库的配置其实不需要额外指定默认就是 H2所以最简化的那个版本也能跑起来。我自己的生产环境用的是 MySQL 方案主要是因为以后有把数据迁移出去的需求H2 的文件格式迁移起来相对麻烦一些。3.2 使用 MySQL 时的完整方案对应热词里的 docker 安装 mysql如果选择外接 MySQL最佳实践是把数据库也做成一个 Docker 容器和 Halo 放在同一个 compose 里管理。这样“复制整个服务”的时候连数据库一块带走备份恢复也统一。MySQL 8.0 版本的配置大致如下halo_db: image: mysql:8.0 container_name: halo_db restart: always command: - --character-set-serverutf8mb4 - --collation-serverutf8mb4_bin - --default-authentication-pluginmysql_native_password volumes: - ./mysql-data:/var/lib/mysql environment: - TZAsia/Shanghai - MYSQL_ROOT_PASSWORD你的根密码 - MYSQL_DATABASEhalo - MYSQL_USERhalo - MYSQL_PASSWORD你的数据库密码几个参数的解释utf8mb4 字符集MySQL 8 默认字符集其实是 utf8mb4但我在command里显式指定一遍更稳妥防止有些系统镜像默认字符集不对。default-authentication-pluginmysql_native_password这个是给老版本客户端兼容用的Halo 的驱动实际上用 caching_sha2_password 也行显式指定是为了少碰几个兼容问题。MYSQL_DATABASE / MYSQL_USER / MYSQL_PASSWORDMySQL 容器首次启动时会自动创建对应的数据库和用户。注意这些环境变量只在“首次初始化”时生效后续就算改了环境变量也不会覆盖已有用户和密码。mysql-data 数据卷数据库文件全部落在这个目录里备份就是打包这个目录删容器、升级镜像数据都还在。把 Halo 服务和 MySQL 服务放到同一个 compose 文件里之后我启动容器就会把两个服务一起带起来不需要单独对待 MySQL。3.3 启动、初始化、登录后台配置文件写好后在/opt/halo目录下执行docker compose up -d-d表示后台运行。第一次启动需要拉取镜像可能会等一小会儿。拉取完成后查看日志确认启动状态docker compose logs -f halo看到类似Halo started successfully的日志就说明服务起来了。浏览器访问http://你的服务器IP会看到 Halo 的初始化页面——设置管理员用户名、密码、邮箱。这里有个多数人不注意的细节初始化页面只能通过“首次安装”进入一旦设置完管理员这个入口就关闭了。如果中途记错了用户名或密码不是重开初始化页面就能解决的得去数据库里改。所以第一次设置密码时建议用一个你记得住的组合或者直接写进密码管理器里。初始化完成后再访问就是登录页了。登录进去你会看到 Halo 的后台——左侧导航、内容管理、外观、插件这几个模块干净得有点不像博客系统。3.4 发布第一篇文章在后台左侧点击“文章” - “新建”进入编辑器。这个编辑器就是我前面说的极简感的来源左侧是 Markdown 编辑区支持标准的 Markdown 语法如#标题、**加粗、代码块等。右侧是实时预览区渲染效果所见即所得。编辑器自带文章摘要、封面图、分类、标签的填写区域但默认收起不干扰创作。写完直接点“发布”文章就上线了。Halo 2.x 默认自带的主题是Earth风格非常干净卡片式列表配大留白基本就是“极简”二字的具象化。如果你连默认主题都不满意去“外观”-“主题”里可以装更多主题或者直接编辑自定义 CSS 微调。但我个人建议第一个月先用默认主题等写作习惯稳定了再去折腾外观不然很容易陷入“调主题两小时、写文章十分钟”的怪圈。4. 日常运维、备份与常见问题速查服务跑起来只是开始真正的考验在后续的维护。这部分我把我这一年多里实际遇到的问题和应对方法整理出来你迟早用得上。4.1 备份与恢复最重要的习惯私有化部署最大的风险不是软件坏了而是数据没了。写作平台的数据在别人服务器上你不用管备份自建博客的数据在自己机器上备份就是你一个人的责任。我经历过一次服务器商数据丢失的事故从那之后对备份格外看重。Halo 的数据构成很简单就两块数据库所有文章内容、用户信息、评论、设置都在里面。附件目录你上传的图片等文件默认存放在/.halo/attachments目录。所以备份就是“打包数据库 打包附件目录”。如果你用 H2 内嵌数据库那更简单整个.halo目录就是一个完整的数据快照。我用的是最朴素的方案写一个简单的备份脚本每天凌晨把/opt/halo里除mysql-data之外的关键目录打包然后上传到自己的对象存储或者另一台机器#!/bin/bash DATE$(date %Y%m%d%H%M%S) tar -czf /backup/halo-$DATE.tar.gz -C /opt/halo .halo mysql-data如果是外接 MySQL 的方案还可以用mysqldump做逻辑备份只导出数据不导出整个文件目录备份文件更小docker exec halo_db mysqldump -u root -p你的根密码 halo /backup/halo-db-$DATE.sql恢复的时候把备份文件解压回/opt/halo对应目录然后docker compose up -d重建容器数据库和附件就能恢复。恢复之前务必关闭服务避免写入新数据把旧数据覆盖掉。提示备份的黄金法则是“3-2-1”——至少 3 份备份、存储在 2 种不同介质上、其中 1 份存放在异地。对个人博客来说本地存一份 云端传一份就已经比绝大多数人的备份习惯强了。4.2 升级 Halo 版本的正确姿势Halo 的迭代速度不算快每年会有几个大版本更新一般都带来新功能或性能优化。升级流程并不复杂但要按正确顺序来备份数据参考上一节。编辑docker-compose.yml把image: halohub/halo:2.17改成新版本号。执行docker compose pull拉取新镜像。执行docker compose up -dcompose 会停止旧容器、创建新容器。观察日志和网站状态确认升级成功。升级后最容易出现的问题是主题或插件不兼容新版本。Halo 的插件系统有自己的 API 版本约束如果某个插件没有跟上主版本更新可能在后台报错或界面异常。遇到这种情况先禁用对应插件等插件作者更新或者暂时不升级 Halo 等插件兼容了再升。所以我在实际升级前通常会把版本号往后调一个周期观察社区反馈而不是官方一发版就马上升。4.3 常见问题速查表我在折腾 Halo 的过程中遇到过不少问题也看过群里别人问过的问题。整理成表格如下方便对照排查现象大概率原因排查/解决方法拉取镜像很慢或超时未配置镜像加速器或加速地址失效配置/etc/docker/daemon.json中的加速地址重启 Docker容器启动失败报端口被占用宿主机 80 或 8080 端口被其他服务占用sudo netstat -tlnp | grep :80找到占用进程后停掉或换映射端口容器一直重启循环数据目录权限不对或数据库连接失败检查.halo目录属主sudo chown -R $USER:$USER .halo检查 MySQL 容器的用户名密码是否一致外网无法访问网站云服务器安全组未放行端口或服务器防火墙拦截登录云控制台在安全组里放行 80/443 入方向sudo ufw status检查系统防火墙时间和实际差 8 小时容器时区未设置在 environment 里加TZAsia/Shanghai并重建容器登录后台后无法上传图片附件目录无写权限检查.halo/attachments目录权限属主改为运行容器的用户这里特别说一下 Docker 权限问题。很多刚开始用 Docker 的朋友会碰到类似Got permission denied while trying to connect to the Docker daemon socket的报错这就是当前用户不在 docker 组导致的。前面安装完已经加了用户组如果还是报这个错就先sudo usermod -aG docker $USER然后重新登录 SSH。如果是在 Windows 上用 Docker Desktop权限模型不一样但 Halo 部署逻辑是相同的只是挂载目录需要设置成共享文件夹里的路径。Docker 网络不通是另一个高频问题。如果你在同一台服务器上跑了多个容器容器之间要互相访问我建议把他们都放进同一个 compose 网络里Halo 连接 MySQL 时直接写服务名halo_db加端口3306而不是写localhost。因为容器内部的localhost指的是容器自己不是宿主机这是新手最容易理解错的地方。5. 让“极简写作神器”更顺手的一些细节服务稳定上线、文章也能正常发布后我开始根据自己的使用习惯做了一些小调整让 Halo 更贴合“写作神器”的定位。分享几个我觉得值回票价的细节。5.1 打开 HTTPS别让地址栏吓跑访客现在浏览器对 HTTP 网站的提示越来越不友好访问自带博客如果是裸 HTTP地址栏会直接标“不安全”。部署 HTTPS 已经是基本要求。如果用了域名最省事的方案是给服务器套一层反向代理比如 Nginx 或者 Caddy再用 Let’s Encrypt 申请免费证书自动续期。如果不想装额外的东西Halo 本身就支持配置 HTTPS 证书也支持在后台直接搞定 HTTP 自动跳转 HTTPS。老实说这个过程对于第一次操作的人有一点繁琐但做一次之后就是一劳永逸的事。5.2 把写作入口变成你的首页快捷方式Halo 后台地址默认是/consoleHalo 2.x每次要手动输入有点烦。我做了两件事第一在浏览器书签里直接收藏http://你的域名/console第二手机桌面上把 URL 快捷方式添加为“博客写作”点开就是登录页。手机上打开直接就是后台码字、传图、发布一气呵成比装一堆 App 方便多了。5.3 评论、统计和搜索按需加装极简不等于“没有任何功能”而是“每个功能都是你需要的”。我自己装了三个插件评论插件gitalk 或 halo-comment访客可以留言交流但界面很干净。访问统计umami 或者内置统计插件能看到每天来了多少人、看了哪些文章至少知道自己的内容有没有人看。本地搜索插件文章多了之后站内搜索是刚需。这三个插件装完博客在功能上已经满足了我 99% 的需求。至于相册、商城、会员系统这些Halo 插件市场里也有但我至今没装过——它们对写作这个核心目标没有帮助只会增加噪音。5.4 一年之后的真实感受用 Halo 这一年多来我最大的感受不是“这个软件好强大”而是“它终于安静地退到了幕后”。写作工具这个类目本质上不需要多少存在感。打开编辑器光标闪动内容可以从大脑直接流到屏幕上这就是最好的状态。Halo 做到了。如果你也想搭一个属于自己的、纯粹的写作角落按这篇文章的顺序操作一个小时左右应该就能把服务跑起来。剩下的事情就交给时间和文字本身吧。

关于本文作者

来自尧图内容编辑团队

尧图内容编辑团队 内容团队

尧图内容编辑团队

本文由尧图网络内容编辑团队执笔。团队由资深项目经理、前端工程师与设计师组成,所有内容均来自亲手交付的真实项目,先讲清问题、再给出可落地的解法。尧图深耕北京网站建设十年,服务过京华建材集团、智造科技等各行业客户,把一线经验沉淀为可复用的行业观察。

  • 十年建站经验,覆盖建材、制造、服务、文创等
  • 项目经理把关选题与事实准确性
  • 工程师与设计师联合撰写专业细节
  • 统一编辑规范,保证文风与排版一致
  • 每月复盘转化数据,迭代选题方向

延伸阅读

相关资讯与近期热门内容

深度阅读推荐

建站决策前值得细读的三篇

网站改版的5个关键决策
2024-08-12

网站改版的5个关键决策

什么时候该改版、改到什么程度、如何避免流量掉光,京华建材集团改版复盘给出答案。

获取专属建站方案

看完文章,把您的行业与预算告诉我们,免费获取一份量身定制的官网建设方案与报价。

立即免费咨询