很多人想搭建自己的技术博客,但一开始往往会被服务器、域名、数据库、反向代理、HTTPS 证书这些概念劝退。

其实,如果只是搭建一个稳定、可维护、适合长期写技术文章的个人博客,并不需要一上来就折腾复杂架构。本文会带你从零开始,使用一台云服务器、一个域名,以及 Docker Compose 部署一套完整的个人博客系统。

本文最终采用的技术架构是:

Halo + PostgreSQL + Nginx Proxy Manager + HTTPS

最终效果是:

www.your-domain.com
http://www.your-domain.com

可以正常访问博客首页,并且可以通过:

https://www.your-domain.com/console

进入 Halo 后台管理页面。

本文适合没有建站经验的新手阅读。只要你准备好一台云服务器和一个域名,按照文章步骤完成服务器配置、域名解析、Docker 部署和 HTTPS 配置,就可以搭建出属于自己的个人技术博客。


一、整体架构

本次部署采用 Docker Compose 管理博客相关服务,整体结构如下:

云服务器
├── Nginx Proxy Manager
│   ├── 80   HTTP
│   ├── 443  HTTPS
│   └── 81   管理后台
│
├── Halo
│   └── 8090 内部端口
│
└── PostgreSQL
    └── 5432 内部端口

访问链路如下:

用户浏览器
    ↓
https://www.your-domain.com
    ↓
Nginx Proxy Manager
    ↓
Halo:8090
    ↓
PostgreSQL

这里没有直接把 Halo 的 8090 端口暴露到公网,而是通过 Nginx Proxy Manager 做反向代理。

这样做有几个好处:

  1. 统一管理 80443 端口;

  2. 统一申请和续期 SSL 证书;

  3. Halo 服务不直接暴露到公网;

  4. PostgreSQL 数据库不暴露公网端口;

  5. 后续迁移、备份和排错更加清晰。

尤其需要注意的是:PostgreSQL 只在 Docker 内部网络中提供服务,外部用户无法直接访问数据库。这比把 5432 端口暴露到公网更安全。


二、部署流程总览

搭建个人博客大致分为三个阶段。

2.1 前期准备

  1. 购买云服务器;

  2. 购买域名;

  3. 配置域名解析;

  4. 将域名指向服务器;

  5. 配置服务器防火墙端口。

2.2 服务器部署

  1. 安装 Docker;

  2. 创建 Docker 网络;

  3. 部署 Nginx Proxy Manager;

  4. 部署 PostgreSQL 数据库;

  5. 部署 Halo 并连接 PostgreSQL;

  6. 启动 Halo 和 PostgreSQL;

  7. 验证数据库与 Halo 服务是否正常;

  8. 在 Nginx Proxy Manager 中配置反向代理;

  9. 访问 Halo 初始化页面。


三、前期准备

3.1 购买云服务器

云服务器可以理解为博客的“主机”。博客程序、数据库、反向代理服务都会运行在这台服务器上。

阿里云、腾讯云、百度云等平台都可以购买云服务器。如果购买的是国内服务器,并且希望绑定自己的域名,通常需要进行备案。备案流程相对麻烦,审核周期也比较长。

如果只是想快速把个人博客跑起来,建议新手优先选择海外服务器。海外服务器通常不需要备案,购买服务器并完成域名解析后,就可以直接访问。

我这里使用的是腾讯云海外轻量应用服务器,年付价格大约 99 元,适合作为个人博客入门配置。

服务器配置建议如下:

配置项

建议

地域

新加坡、中国香港等海外地域

系统镜像

Ubuntu

CPU / 内存

入门博客 2 核 2G 基本够用

带宽

个人博客访问量不大,轻量配置即可

价格

新手可以优先选择云厂商优惠套餐

这里建议系统镜像选择 Ubuntu。Ubuntu 的资料比较多,Docker、Nginx、Halo 等相关问题也更容易搜索到解决方案。

如果你不知道近期有哪些便宜服务器套餐,也可以关注一些专门分享云服务器优惠信息的 B 站 UP 主,他们会定期整理腾讯云、阿里云、百度云等平台的活动。


3.2 购买域名

如果说服务器是博客的“房子”,那么域名就是这套房子的“门牌号”。

用户最终访问博客时,通常不会直接输入服务器 IP,而是输入类似下面这样的域名:

www.example.top
example.top

腾讯云、阿里云、百度云等平台都可以购买域名。如果只是个人博客入门,.top.site.xyz 这类后缀价格较低,也可以使用。

我这里使用的是百度云购买的 .top 域名,首年价格大约 15 元,比较适合作为个人博客入门域名。

购买域名时建议注意:

  1. 域名尽量简短,方便记忆;

  2. 尽量避免复杂拼音或数字组合;

  3. 如果只是个人博客,便宜后缀也够用;

  4. 如果想长期经营个人品牌,可以优先考虑 .com


3.3 配置域名解析

购买域名之后,域名本身还不能直接访问你的服务器。你需要将域名解析到服务器的公网 IP。

简单理解就是:

用户访问 www.example.top
        ↓
DNS 解析到服务器公网 IP
        ↓
请求到达云服务器
        ↓
Nginx Proxy Manager 转发到 Halo
        ↓
返回博客页面

建议至少添加两条 A 记录:

主机记录

记录类型

记录值

www

A 记录

服务器公网 IP

@

A 记录

服务器公网 IP

这两条解析的作用分别是:

www.example.top  → 服务器公网 IP
example.top      → 服务器公网 IP

这样用户无论输入带 www 的域名,还是不带 www 的域名,都可以访问到你的博客。


3.4 在百度云配置域名解析

如果你的域名是在百度云购买的,可以在百度智能云控制台中配置解析。

3.4.1 进入域名服务

在百度智能云控制台中搜索并进入 域名服务

3.4.2 找到域名管理

进入域名服务后,在左侧选择 域名管理,然后在右侧找到你购买的域名,点击 解析

3.4.3 添加 www 解析记录

点击 添加解析,填写第一条记录:

配置项

填写内容

主机记录

www

记录类型

A 记录

解析线路

默认

记录值

服务器公网 IP

TTL

默认即可

这条记录的作用是:让用户访问 www.example.top 时,可以进入你的服务器。

3.4.4 添加 @ 解析记录

继续添加第二条记录:

配置项

填写内容

主机记录

@

记录类型

A 记录

解析线路

默认

记录值

服务器公网 IP

TTL

默认即可

这条记录的作用是:让用户访问 example.top 时,也可以进入你的服务器。

最终建议至少添加两条解析:

www.example.top  → 服务器公网 IP
example.top      → 服务器公网 IP

3.5 腾讯云服务器侧配置域名

如果你使用的是腾讯云轻量应用服务器,也可以在腾讯云控制台中把域名添加到对应服务器实例中,方便后续统一管理。

3.5.1 进入轻量应用服务器控制台

登录腾讯云控制台,点击左上角菜单,搜索或选择 轻量应用服务器

3.5.2 添加域名解析

进入服务器实例后,选择:

域名解析 → 添加域名解析

在弹窗中输入你刚刚购买的域名,例如:

example.top

系统通常会自动生成两条主机记录:

www.example.top
example.top

确认无误后点击确定。

需要注意:真正决定域名能不能访问服务器的是 DNS 解析是否生效。如果你的域名不是在腾讯云购买的,仍然要以域名购买平台的解析配置为准。


3.6 配置服务器防火墙端口

博客部署过程中至少需要开放以下端口:

端口

协议

作用

22

TCP

SSH 登录服务器

80

TCP

HTTP 访问与证书申请

443

TCP

HTTPS 访问

81

TCP

Nginx Proxy Manager 管理后台

其中:

  • 80443 是网站访问必须使用的端口;

  • 81 是 Nginx Proxy Manager 的管理后台端口;

  • 22 是远程连接服务器的 SSH 端口。


四、服务器部署阶段

4.1 安装 Docker

本文使用 Docker Compose 管理 Halo、PostgreSQL 和 Nginx Proxy Manager。

先检查 Docker 是否已经安装:

docker -v
docker compose version

如果没有安装,可以执行:

curl -fsSL https://get.docker.com | sudo bash
sudo systemctl enable docker
sudo systemctl start docker

为了后续使用 Docker 命令更方便,可以将当前用户加入 Docker 用户组:

sudo usermod -aG docker $USER

这个命令不会立刻对当前 SSH 会话生效。你可以重新登录服务器,或者后续命令先使用 sudo docker

如果执行:

docker ps

出现类似下面的错误:

permission denied while trying to connect to the docker API

可以先使用:

sudo docker ps

4.2 创建 Docker 网络

Nginx Proxy Manager 和 Halo 需要在同一个 Docker 网络中通信,因此先创建一个公共网络:

sudo docker network create npm

如果提示网络已经存在,可以忽略。

这个 npm 网络后面会同时挂载到:

Nginx Proxy Manager 容器
Halo 容器

这样 Nginx Proxy Manager 才能通过容器名 halo 访问 Halo 服务。


4.3 部署 Nginx Proxy Manager

Nginx Proxy Manager 用来管理反向代理和 HTTPS 证书。

先创建目录:

mkdir -p ~/npm
cd ~/npm
vim docker-compose.yml

写入以下内容:

services:
  npm:
    image: 'jc21/nginx-proxy-manager:latest'
    container_name: npm
    restart: unless-stopped
    ports:
      - '80:80'
      - '443:443'
      - '81:81'
    volumes:
      - ./data:/data
      - ./letsencrypt:/etc/letsencrypt
    networks:
      - npm

networks:
  npm:
    external: true

启动 Nginx Proxy Manager:

sudo docker compose up -d

检查容器状态:

sudo docker ps

如果正常,会看到类似:

jc21/nginx-proxy-manager:latest   Up   0.0.0.0:80-81->80-81/tcp, 0.0.0.0:443->443/tcp   npm

检查端口:

sudo ss -lntp | grep -E ':80|:443|:81'

如果看到 docker-proxy 占用了 8044381,说明 Nginx Proxy Manager 已经正常接管端口。

浏览器访问:

http://服务器公网IP:81

第一次进入会要求创建管理员账号:

Full Name:自定义
Email address:自己的邮箱
New Password:自定义强密码

注意:这个账号是 Nginx Proxy Manager 的管理员账号,用来管理反向代理和 SSL 证书,不是 Halo 博客后台账号。


4.4 部署 PostgreSQL 数据库

Halo 需要数据库来持久化保存站点配置、用户信息、文章内容和主题配置。

本文不使用宝塔面板安装 PostgreSQL,也不在宿主机上手动安装数据库,而是通过 Docker Compose 启动一个独立的 PostgreSQL 容器。

这样做有几个好处:

  1. 数据库环境独立,不污染宿主机;

  2. 部署和迁移更方便;

  3. 数据目录清晰,便于备份;

  4. Halo 和 PostgreSQL 可以通过 Docker 内部网络通信;

  5. PostgreSQL 不需要暴露公网端口,安全性更高。

在后面的 docker-compose.yml 中,PostgreSQL 服务由下面这一段定义:

halodb:
  image: postgres:15.4
  container_name: halodb
  restart: on-failure:3
  networks:
    - halo_network
  volumes:
    - ./db:/var/lib/postgresql/data
  healthcheck:
    test: ["CMD", "pg_isready"]
    interval: 10s
    timeout: 5s
    retries: 5
  environment:
    - POSTGRES_PASSWORD=你的数据库强密码
    - POSTGRES_USER=halo
    - POSTGRES_DB=halo
    - PGUSER=halo

其中几个关键配置如下:

配置项

含义

POSTGRES_USER=halo

自动创建数据库用户 halo

POSTGRES_PASSWORD=你的数据库强密码

设置 halo 用户的数据库密码

POSTGRES_DB=halo

自动创建名为 halo 的数据库

./db:/var/lib/postgresql/data

将数据库数据持久化到宿主机目录

pg_isready

用于检测 PostgreSQL 是否启动完成

第一次执行:

sudo docker compose up -d

PostgreSQL 容器会自动完成以下初始化工作:

  1. 拉取 postgres:15.4 镜像;

  2. 创建 halodb 容器;

  3. 初始化数据库数据目录;

  4. 创建 halo 用户;

  5. 创建 halo 数据库;

  6. 设置数据库密码;

  7. 将数据库数据持久化保存到 ~/halo/db

因此,这里不需要进入宝塔面板创建数据库,也不需要手动执行 SQL 创建数据库。


4.5 部署 Halo 并连接 PostgreSQL

创建 Halo 目录:

mkdir -p ~/halo
cd ~/halo
vim docker-compose.yml

写入完整配置。

需要修改两个地方:

  1. 数据库强密码;

  2. --halo.external-url=https://www.your-domain.com/

完整配置如下:

services:
  halo:
    image: registry.fit2cloud.com/halo/halo:2.24
    container_name: halo
    restart: on-failure:3
    depends_on:
      halodb:
        condition: service_healthy
    networks:
      - halo_network
      - npm
    volumes:
      - ./halo2:/root/.halo2
    expose:
      - "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=你的数据库强密码
      - --spring.sql.init.platform=postgresql
      - --halo.external-url=https://www.your-domain.com/

  halodb:
    image: postgres:15.4
    container_name: halodb
    restart: on-failure:3
    networks:
      - halo_network
    volumes:
      - ./db:/var/lib/postgresql/data
    healthcheck:
      test: ["CMD", "pg_isready"]
      interval: 10s
      timeout: 5s
      retries: 5
    environment:
      - POSTGRES_PASSWORD=你的数据库强密码
      - POSTGRES_USER=halo
      - POSTGRES_DB=halo
      - PGUSER=halo

networks:
  halo_network:
  npm:
    external: true

其中,Halo 连接 PostgreSQL 的关键配置是:

command:
  - --spring.r2dbc.url=r2dbc:pool:postgresql://halodb/halo
  - --spring.r2dbc.username=halo
  - --spring.r2dbc.password=你的数据库强密码
  - --spring.sql.init.platform=postgresql

含义如下:

配置项

含义

postgresql://halodb/halo

连接 halodb 容器中的 halo 数据库

spring.r2dbc.username=halo

使用 halo 数据库用户

spring.r2dbc.password=你的数据库强密码

使用 PostgreSQL 中设置的密码

spring.sql.init.platform=postgresql

指定数据库类型为 PostgreSQL

这里有一个非常关键的点:

spring.r2dbc.password 和 POSTGRES_PASSWORD 必须完全一致。

否则 Halo 会因为数据库认证失败而无法启动。


4.6 启动 Halo 和 PostgreSQL

~/halo 目录下执行:

sudo docker compose up -d

这个命令会同时启动两个容器:

容器

作用

halo

Halo 博客服务

halodb

PostgreSQL 数据库

查看容器状态:

sudo docker ps

正常情况下应该看到:

halo     Up healthy    8090/tcp
halodb   Up healthy    5432/tcp
npm      Up            80/81/443

其中:

halodb healthy

说明 PostgreSQL 数据库已经启动成功。

halo healthy

说明 Halo 已经成功连接数据库,并且应用本身启动正常。


4.7 验证 PostgreSQL 是否初始化成功

可以通过下面命令进入 PostgreSQL 容器:

sudo docker exec -it halodb psql -U halo -d halo

进入后,如果看到类似:

halo=#

说明已经成功连接到 halo 数据库。

可以执行:

\l

查看数据库列表。

也可以执行:

\dt

查看当前数据库中的表。

如果 Halo 已经启动过,数据库中会出现 Halo 自动创建的数据表。

退出 PostgreSQL:

\q

如果能成功进入 psql,说明以下内容都已经正常:

  1. PostgreSQL 容器启动成功;

  2. halo 用户创建成功;

  3. halo 数据库创建成功;

  4. 数据库密码配置正确;

  5. 数据库数据目录已经持久化到 ~/halo/db

需要注意的是,PostgreSQL 的 5432 端口没有映射到公网。

在本架构中,Halo 和 PostgreSQL 通过 Docker 内部网络 halo_network 通信,外部用户无法直接访问数据库。这比将数据库端口暴露到公网更安全。


4.8 检查 Halo 是否启动成功

通过健康检查接口确认:

sudo docker exec -it halo curl -s http://localhost:8090/actuator/health/readiness

正常返回:

{"status":"UP"}

这说明 Halo 本身已经正常运行。

如果返回异常,可以查看 Halo 日志:

sudo docker logs -f halo

如果是数据库连接失败,重点检查:

  1. POSTGRES_PASSWORDspring.r2dbc.password 是否一致;

  2. halodb 容器是否 healthy;

  3. Halo 和 PostgreSQL 是否在同一个 halo_network 网络中;

  4. spring.r2dbc.url 中的主机名是否写成了 halodb


4.9 在 Nginx Proxy Manager 中添加反向代理

现在 Halo 已经在 Docker 内部运行,但外部用户还不能直接通过域名访问。

接下来需要在 Nginx Proxy Manager 中添加反向代理,将域名请求转发到 Halo 容器。

浏览器访问:

http://服务器公网IP:81

进入 Nginx Proxy Manager 后,选择:

Hosts → Proxy Hosts → Add Proxy Host

填写代理信息。

Details 配置

配置项

填写内容

Domain Names

www.your-domain.com

Scheme

http

Forward Hostname / IP

halo

Forward Port

8090

Cache Assets

可不勾选

Block Common Exploits

建议勾选

Websockets Support

建议勾选

这里最关键的是:

Forward Hostname / IP = halo
Forward Port = 8090

因为 Nginx Proxy Manager 和 Halo 在同一个 Docker 网络 npm 中,所以 Nginx Proxy Manager 可以直接通过容器名 halo 访问 Halo 服务。

如果你还想让不带 www 的域名也能访问,也可以在 Domain Names 中同时填写:

your-domain.com
www.your-domain.com

4.10 访问 Halo 初始化页面

配置完成后,浏览器访问:

https://www.your-domain.com

正常情况下会进入 Halo 初始化页面。

后台地址为:

https://www.your-domain.com/console

第一次进入 Halo,需要创建 Halo 管理员账号。

注意区分:

账号

用途

Nginx Proxy Manager 管理员账号

管理反向代理和 SSL 证书

Halo 管理员账号

管理博客内容、页面、主题和用户

这两个账号不是同一个。


五、总结

以上是一套适合个人的技术博客部署方案:

Halo + PostgreSQL + Nginx Proxy Manager + HTTPS

最终访问链路如下:

用户访问域名
    ↓
Nginx Proxy Manager 处理 HTTPS
    ↓
转发到 Halo 容器 8090
    ↓
Halo 读写 PostgreSQL

这套架构的核心思想是:每个组件只负责自己最擅长的事情。

Docker Compose 负责服务编排
Nginx Proxy Manager 负责反向代理和 HTTPS
Halo 负责博客内容管理
PostgreSQL 负责数据持久化

本文中的 PostgreSQL 是作为 Docker Compose 中的一个独立服务运行。

Halo 容器启动时,通过 Docker 内部服务名 halodb 访问 PostgreSQL,因此数据库不需要暴露公网端口。

这种部署方式结构清晰、迁移方便、排错简单,也更适合作为个人技术博客的长期维护方案。