直接回答:把 Hexo 博客部署到阿里云 ECS 需要四步——域名解析到服务器、服务器装好 Nginx、本地构建后用 rsync 同步静态文件、申请 HTTPS 证书。整个过程可以固化成一个脚本,之后每次发布只需要一条命令。
准备工作
开始之前需要确认四件事:
| 项目 | 说明 |
|---|---|
| 一台 ECS 实例 | 1 核 2G 起步;系统选 Ubuntu 22.04 或 Alibaba Cloud Linux |
| 备案 | 域名指向中国大陆服务器需要完成 ICP 备案 |
| 安全组 | 放行 80 和 443 端口(22 用于 SSH) |
| 本地环境 | 能正常 npm run build 生成 public/ 目录 |
备案是最容易被忽略的时间成本。 如果还没备案,建议先去走流程,等备案通过期间可以先把博客搭起来。
一、域名解析
在域名管理后台为博客添加一条记录:
| 记录类型 | 主机记录 | 记录值 |
|---|---|---|
| A | devnotes |
你的 ECS 公网 IP |
生效后本机验证:
1 | dig +short devnotes.cuiyuehui.cn |
二、服务器初始化
1 | # 安装 Nginx 与同步工具 |
阿里云还需要在控制台的安全组里放行 80 / 443,只配服务器内的防火墙是不够的。
三、Nginx 配置
创建 /etc/nginx/conf.d/devnotes.conf:
1 | server { |
检查并生效:
1 | sudo nginx -t |
关于缓存的一个教训
我一开始给所有文件都加了长缓存,结果每次发布新文章,读者看到的还是旧的首页。HTML 必须不缓存,带指纹的资源才应该长缓存。 上面配置里把 .html 单独设成 no-cache,就是踩了这个坑之后加上去的。
四、申请 HTTPS 证书
用 Certbot 自动申请并配置续期:
1 | sudo apt install -y certbot python3-certbot-nginx |
Certbot 会自动改写 Nginx 配置、加上跳转,并注册一个定时续期任务。验证续期是否正常:
1 | sudo certbot renew --dry-run |
也可以使用阿里云签发的免费证书,在控制台下载后手动配置 ssl_certificate 与 ssl_certificate_key,效果相同。
五、部署脚本
在博客目录创建 tools/deploy.sh:
1 |
|
几个关键参数:
| 参数 | 作用 |
|---|---|
--delete |
删除服务器上本地已不存在的文件,避免旧页面残留 |
-z |
传输压缩,提升跨地域同步速度 |
--exclude |
排除系统残留文件 |
第一次部署前先做一次空跑确认删除范围:
1 | rsync -avz --delete --dry-run public/ "${SERVER}:${TARGET}" |
--dry-run 会列出所有将被删除的文件。在没有确认之前不要去掉这个参数——路径写错一个字符,可能删光整个目录。
六、评论服务(可选)
如果希望读者能直接留言,可以在同一台服务器上用 Docker 跑一套 Waline + MySQL。
一份 docker-compose.yml 同时拉起两个容器,MySQL 不对外暴露端口:
1 | # docker-compose.yml |
密码不要写在 compose 文件里,放在同目录的 .env(记得加进 .gitignore):
1 | MYSQL_ROOT_PASSWORD=随机串 |
Waline 自己不会建表。 需要把官方的 waline.sql
放在 compose 同目录,MySQL 首次初始化时会自动导入,建出
wl_Comment / wl_Counter / wl_Users 三张表。少了它,接口会直接返回
500 no such table: wl_Comment——这是我在本地实测时踩到的第一个坑。
配合前面 Nginx 里的 /comments/ 反代,前端配置只需要填:
1 | comments: |
同域反代的好处是不用处理跨域,也不需要再申请一个子域名和证书。
部署完记得打开 https://devnotes.cuiyuehui.cn/comments/ui/register 注册管理员账号
——第一个注册的账号自动成为管理员,注册后建议关闭注册入口。
七、备份
静态站点本身不需要备份(源文件在 Git 里),但有两样东西要定期备份:评论数据库、Nginx 与证书配置。
留言在 MySQL 里,用 mysqldump 导出成压缩文件,每天备份一次并只保留最近 14 天。
--single-transaction 保证备份期间不锁表,服务不用停:
1 |
|
恢复时把备份解开灌回去即可:
1 | gunzip -c /backup/waline/waline-2026-09-20_0300.sql.gz \ |
waline.sql、docker-compose.yml、backup-waline.sh这几个文件在仓库的deploy/waline/目录里都有现成的,不用自己敲。
踩坑记录
| 现象 | 原因 | 解决 |
|---|---|---|
| 部署后访问 403 | 目录权限或 SELinux 限制 | 确认 Nginx 用户可读;必要时调整 SELinux 策略 |
| 首页一直是旧内容 | HTML 被长缓存 | 对 .html 设置 no-cache |
| 新文章 404 | 构建失败或同步遗漏 | 检查 public/ 里是否真的生成了文件 |
| 证书续期失败 | 80 端口被别的服务占用 | 释放 80 端口后重跑 certbot renew |
| 部署很慢 | 每次都全量传输 | 使用 -z 并确认增量同步生效 |
留言接口返回 500 ER_NOT_SUPPORTED_AUTH_MODE |
MySQL 账号用了 caching_sha2_password 认证,Waline 的驱动不支持 |
MySQL 启动参数加上 --default-authentication-plugin=mysql_native_password |
留言接口返回 500 no such table |
没有导入建表脚本 | 把官方 waline.sql 放进 compose 同目录,MySQL 首次启动会自动导入 |
常见问题(FAQ)
静态博客需要多大配置的服务器?
1 核 2G 足够。Nginx 服务静态文件的资源消耗极低,一台最低配的 ECS 可以轻松支撑每天数千次访问。真正吃资源的是评论服务,如果需要自建 Waline 并启用数据库,建议 2 核 4G 起步。
为什么要用 rsync 而不是直接覆盖目录?
rsync 只传输变化过的文件,一次增量发布通常只需要几秒,而且不会把没在本地生成的文件误删。直接覆盖目录在文件多的时候会明显变慢,也容易留下旧文件。
部署后访问出现 403 或 404 是什么原因?
绝大多数是 Nginx 的 root 路径指向错误,或者文件权限不对。先确认 root 指向的目录里确实有 index.html,再确认 Nginx 运行用户对该目录有读取权限。
小结
- 顺序是:解析 → 装 Nginx → 同步文件 → 上证书。
- 缓存策略记住一条:HTML 不缓存,带指纹的资源长缓存。
- 用 rsync 增量同步,改动后用
--dry-run确认。 - 同域反代评论服务,省掉跨域和第二个证书。
完整的配置文件和脚本都在本站的 deploy/ 与 tools/ 目录里,可以直接取用。部署过程中卡在哪一步,欢迎到留言板说说具体报错。
如果站点还没搭起来,先看《用 Hexo + Stellar 搭一个对 AI 友好的技术博客》;如果之后要改地址结构,注意看《站点改版后 URL 变了,怎么保住已有的引用》。