1804 字
9 分钟
搭建 Waline 评论系统
一、背景与整体架构说明
1.博客部署背景
当前博客采用 双部署 + DNS 分流 的方式:
- 海外访问
- Cloudflare Pages
- 国内访问
- 自建服务器(Nginx)
- DNS
- 按地域解析(国内 / 海外)
博客技术栈:
- Astro
- Fuwari 主题
2.评论系统目标
- 无需登录即可评论(匿名)
- 国内 / 海外访问稳定
- 与博客前端部署方式解耦
- 数据可控,可长期维护
- 能在低配置服务器上稳定运行
3.最终方案选择
Docker 部署 Waline + PostgreSQL 数据库 + 独立评论域名
为什么选择 Waline + PostgreSQL(而不是 Twikoo / Mongo)1. 为什么选择 Waline
Waline 的定位非常明确:为自建博客服务的轻量评论系统。
它的关键特性正好命中需求:
- 官方支持自建
- 前后端完全解耦
- 原生支持匿名评论
- Node.js 架构,资源占用可控
- 社区成熟、长期维护
2. 为什么数据库选 PostgreSQL
在低配置服务器上,数据库选择尤为重要:
数据库 是否推荐 原因 PostgreSQL 强烈推荐 内存可控、稳定、并发友好 MySQL 可选 同样稳定,但 PG 在复杂查询上更强 SQLite 仅低流量 并发写入能力有限 MongoDB 不推荐同机 常驻内存大,2G 机器容易吃紧 结论:
如果数据库和 Waline 在同一台服务器上 —— PostgreSQL 是最稳妥的选择。
二、整体系统架构
浏览器用户 ────▶ example.com Astro / Fuwari │ ▼ comment.example.com │ ▼ Nginx (HTTPS) │ ▼ Docker: Waline Server │ ▼ Docker: PostgreSQL评论系统必须使用独立域名
示例:
comment.example.com
- 评论系统与博客主站完全解耦
- 前端无论部署在 Cloudflare Pages 还是自建服务器,都访问该域名
DNS 分流策略
| 区域 | comment.example.com 指向 |
|---|---|
| 国内 | 国内服务器 IP |
| 海外 | Cloudflare / 海外入口 |
三、安装 Docker(阿里云镜像)
安装 Docker(阿里云镜像)
yum install -y yum-utilsyum-config-manager --add-repo https://mirrors.aliyun.com/docker-ce/linux/centos/docker-ce.repoyum install -y docker-ce-20.10.24 docker-ce-cli-20.10.24 containerd.io启动并设置开机自启
systemctl start dockersystemctl is-enabled dockerdocker -v配置阿里云 Docker 镜像加速器
进入阿里云控制台:容器镜像服务 ACR → 镜像工具 → 镜像加速器
会给你一个专属地址,形如:https://xxxxxx.mirror.aliyuncs.com

mkdir -p /etc/docker
cat >/etc/docker/daemon.json <<'EOF'{ "registry-mirrors": [ "https://docker.m.daocloud.io", "https://xxxxxx.mirror.aliyuncs.com" ], "dns": ["223.5.5.5", "114.114.114.114"]}EOF说明:
registry-mirrors:配置阿里云镜像加速器(你提供的地址)dns:避免某些环境下拉镜像时的 DNS 抖动(加了更稳)docker.m.daocloud.io:DaoCloud 镜像加速器,拉取官方镜像更快。
systemctl daemon-reloadsystemctl restart docker四、Docker 部署 PostgreSQL + Waline Server
运行目录:/opt/waline/
-
docker-compose.yml -
.env
数据目录:/data/
- PostgreSQL 数据:
/data/postgres
创建目录
mkdir -p /opt/walinemkdir -p /data/postgrescd /opt/waline用 .env 管理敏感信息
在 /opt/waline/.env
vim /opt/waline/.env写入:
# PostgreSQL 数据库名/用户/密码POSTGRES_DB=walinePOSTGRES_USER=walinePOSTGRES_PASSWORD=CHANGE_ME_STRONG_PASSWORD
# Waline :博客站点名称/允许的域名(逗号分隔)SITE_NAME=My BlogALLOWED_ORIGINS=https://blog.example.com,https://cn.blog.example.com,https://comment.example.com
# 评论系统域名COMMENT_SERVER_URL=https://comment.example.com优点:
docker-compose.yml不用写明文密码- 换密码、换域名直接改
.env
docker-compose.yml(PostgreSQL + Waline Server)
在 /opt/waline 目录下创建 docker-compose.yml:
vim /opt/waline/docker-compose.yml写入(PostgreSQL + Waline):
version: "3.9"
services: postgres: # 镜像:使用 DaoCloud 镜像加速,避免拉取缓慢 image: postgres:17.6 container_name: waline-postgres restart: always environment: POSTGRES_DB: ${POSTGRES_DB} POSTGRES_USER: ${POSTGRES_USER} POSTGRES_PASSWORD: ${POSTGRES_PASSWORD} TZ: Asia/Shanghai volumes: - /data/postgres:/var/lib/postgresql/data # 安全建议:如果不需要外部访问 PG,可以不映射端口 # ports: # - "5432:5432" healthcheck: test: ["CMD-SHELL", "pg_isready -U ${POSTGRES_USER} -d ${POSTGRES_DB}"] interval: 10s timeout: 5s retries: 5
waline: image: lizheming/waline:latest container_name: waline restart: always depends_on: postgres: condition: service_healthy environment: TZ: Asia/Shanghai SITE_NAME: ${SITE_NAME} # === 数据库配置(PostgreSQL)=== PG_HOST: postgres # 关键:同 compose 内用服务名互通 PG_PORT: 5432 PG_DB: ${POSTGRES_DB} PG_USER: ${POSTGRES_USER} PG_PASSWORD: ${POSTGRES_PASSWORD}
# === 跨域白名单(国内/海外博客域名都要写)=== ALLOWED_ORIGINS: "${ALLOWED_ORIGINS}"
# === 新评论通知邮件(可选)=== # SMTP(二选一:SMTP_SERVICE 或 SMTP_HOST/SMTP_PORT) # SMTP_SERVICE: "QQ" # 若服务商在 Waline 支持列表里,可用此项(更省事) # SMTP_HOST: "smtp.qq.com" # SMTP_PORT: "465" # SMTP_SECURE: "true"
# SMTP_USER: "xxx@qq.com" # SMTP_PASS: "这里填SMTP授权码/客户端专用密码" # SITE_URL: "网站地址,用于在消息中显示" # SENDER_NAME: "自定义发送邮件的发件人" # SENDER_EMAIL: "自定义发送邮件的发件人邮箱" # AUTHOR_EMAIL: "站长邮箱(关键:用于新评论通知的收件人)"
# === 低配置服务器建议限制 Node 堆内存,避免 OOM === NODE_OPTIONS: "--max-old-space-size=256"
ports: - "8360:8360"
logging: driver: "json-file" options: max-size: "10m" max-file: "3"说明:
DB_HOST: postgres:这里不是127.0.0.1,而是 compose 内部网络的服务名。depends_on + healthcheck:确保 PostgreSQL 就绪后再启动 Waline,减少“启动时连不上数据库”的假故障。- PG 的
ports可注释掉:让数据库只在 Docker 内部可见,更安全。 NODE_OPTIONS:低配置服务器强烈建议加。
启动服务
在 /opt/waline/ 目录执行:
cd /opt/walinedocker compose up -d查看状态:
docker compose ps查看日志(用于排错错误):
docker logs -f walinedocker logs -f waline-postgres常用命令:
# 停止docker compose down
# 重启docker compose restart
# 更新镜像并重建(升级)docker compose pulldocker compose up -dPostgreSQL 初始化
重要说明
使用 PostgreSQL作为 Waline 存储时,需要手动初始化表结构。
- 进入 PostgreSQL 容器
假设数据库容器名为 waline-postgres:
docker exec -it waline-postgres psql -U waline -d waline成功后会进入 PostgreSQL 交互界面:
waline=#- 初始化数据库表
Waline v3 默认表结构:
CREATE SEQUENCE wl_comment_seq;
CREATE TABLE wl_comment ( id int check (id > 0) NOT NULL DEFAULT NEXTVAL ('wl_comment_seq'), user_id int DEFAULT NULL, comment text, insertedAt timestamp(0) without time zone NOT NULL DEFAULT CURRENT_TIMESTAMP, ip varchar(100) DEFAULT '', link varchar(255) DEFAULT NULL, mail varchar(255) DEFAULT NULL, nick varchar(255) DEFAULT NULL, pid int DEFAULT NULL, rid int DEFAULT NULL, sticky numeric DEFAULT NULL, status varchar(50) NOT NULL DEFAULT '', "like" int DEFAULT NULL, ua text, url varchar(255) DEFAULT NULL, createdAt timestamp(0) without time zone NULL DEFAULT CURRENT_TIMESTAMP, updatedAt timestamp(0) without time zone NULL DEFAULT CURRENT_TIMESTAMP, PRIMARY KEY (id));
CREATE SEQUENCE wl_counter_seq;
CREATE TABLE wl_counter ( id int check (id > 0) NOT NULL DEFAULT NEXTVAL ('wl_counter_seq'), time int DEFAULT NULL, reaction0 int DEFAULT NULL, reaction1 int DEFAULT NULL, reaction2 int DEFAULT NULL, reaction3 int DEFAULT NULL, reaction4 int DEFAULT NULL, reaction5 int DEFAULT NULL, reaction6 int DEFAULT NULL, reaction7 int DEFAULT NULL, reaction8 int DEFAULT NULL, url varchar(255) NOT NULL DEFAULT '', createdAt timestamp(0) without time zone NULL DEFAULT CURRENT_TIMESTAMP, updatedAt timestamp(0) without time zone NULL DEFAULT CURRENT_TIMESTAMP, PRIMARY KEY (id));
CREATE SEQUENCE wl_users_seq;
CREATE TABLE wl_users ( id int check (id > 0) NOT NULL DEFAULT NEXTVAL ('wl_users_seq'), display_name varchar(255) NOT NULL DEFAULT '', email varchar(255) NOT NULL DEFAULT '', password varchar(255) NOT NULL DEFAULT '', type varchar(50) NOT NULL DEFAULT '', label varchar(255) DEFAULT NULL, url varchar(255) DEFAULT NULL, avatar varchar(255) DEFAULT NULL, github varchar(255) DEFAULT NULL, twitter varchar(255) DEFAULT NULL, facebook varchar(255) DEFAULT NULL, google varchar(255) DEFAULT NULL, weibo varchar(255) DEFAULT NULL, qq varchar(255) DEFAULT NULL, oidc varchar(255) DEFAULT NULL, "2fa" varchar(32) DEFAULT NULL, createdAt timestamp(0) without time zone NULL DEFAULT CURRENT_TIMESTAMP, updatedAt timestamp(0) without time zone NULL DEFAULT CURRENT_TIMESTAMP, PRIMARY KEY (id));执行完成后,可以检查表是否存在:
\dt正常情况下应看到:
wl_comments | wl_counters | wl_users五、Nginx 反代到子域(HTTPS)
目标
- 外网访问
https://comment.example.com - Nginx 转发到本机
http://127.0.0.1:8360
示例配置:
server { # 80 强制跳转到 443 listen 80; server_name comment.example.com; return 301 https://$host$request_uri;}
server { listen 443 ssl http2; server_name comment.example.com;
# ssl证书 ssl_certificate /path/fullchain.pem; ssl_certificate_key /path/privkey.pem;
location / { proxy_pass http://127.0.0.1:8360; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme; proxy_set_header X-Forwarded-Host $host;
proxy_http_version 1.1; proxy_set_header Connection ""; }}检查并重载 Nginx:
nginx -tnginx -s reload此时访问应可打开:https://comment.example.com
六、配置 Waline 客户端
- 在浏览器访问注册页
https://comment.example.com/ui/register
- 注册账号(首次初始化)
- 第一个成功注册的账号会自动成为管理员
- 注册完成后,即可登录后台
https://comment.example.com/ui
搭建 Waline 评论系统
https://blog.xhwen.cn/posts/blog/waline-docker-postgresql/
评论