Isso Docker Compose 部署指南

在 Linux 服务器上以 Docker Compose 部署 Isso 0.14.0 评论系统:isso 评论服务与演示页双服务编排、独立子网固定 IP、配置只读挂载与 SQLite 数据持久化、嵌入演示页评论链路与反垃圾拦截实测、数据备份与等价 docker run 形态,全程实测回显。

IssoDocker Compose自托管2026/10/30

Isso Docker Compose 部署指南

本文在真实 Linux 服务器上用 Docker Compose 完成 Isso 0.14.0 评论系统的完整部署与验收:isso 评论服务与演示宿主页双服务编排、独立子网固定 IP、配置只读挂载与 SQLite 数据持久化,并实测嵌入演示页、评论提交、反垃圾拦截、待审评论不可见与数据备份全链路。全程命令与回显均为实测记录(实测日期 2026-10-30)。

目标: 可复现地跑通「部署 → 嵌入 → 提交 → 审核 → 备份」闭环。组件定位与选型背景见《Isso 0.14.0 介绍与选型指南》,参数级配置说明见《Isso 0.14.0 配置参数详解》,接口级实测见《Isso 0.14.0 API 使用详解》。

一、部署前准备

1.1 环境信息

项目内容
组件版本Isso 0.14.0(上游最新稳定版,实查 GitHub Releases,查询日期 2026-10-30)
评论服务镜像ghcr.io/isso-comments/isso:release(本地实测 119MB,镜像 ID 8e813ff52004)
演示页镜像nginx:alpine(仅提供静态演示宿主页)
服务器Ubuntu 26.04.1 LTS(x86_64),Docker 引擎 29.9.2,Docker Compose v5.6.1
端口规划宿主 8141 → isso 服务 8080(评论 API 与管理后台);宿主 8142 → demo-page 服务 80(演示页)
部署目录/opt/data/quzuo/tmp/isso(含 config/、db/、page/ 三个子目录与 compose.yaml)
容器命名isso-0.14.0(评论服务)、isso-demo-page(演示页)
容器网络isso-0.14.0-net,显式子网 10.89.71.0/24;isso 固定 10.89.71.10、演示页固定 10.89.71.11
管理后台口令q1w2e3r4(Isso 管理后台仅口令无用户名,本文按演示账号惯例以 admin / q1w2e3r4 指代)

1.2 镜像选型说明

  • 官方镜像发布于 GHCR 的 ghcr.io/isso-comments/isso。0.14.0 起镜像 tag 语义有明确变更(官方 CHANGES #970)::latest 跟随 master 分支每次推送重建,:release 指向最新稳定版。本文选择 :release——部署时其内容即 0.14.0,后文 3.4 节以 /info 接口实测确认。
  • 容器内运行形态(docker inspect 实测):
docker inspect isso-0.14.0 --format 'Entrypoint={{json .Config.Entrypoint}} Cmd={{json .Config.Cmd}}'

实测回显:

Entrypoint=["/isso/bin/gunicorn","-b","0.0.0.0:8080","-w","4","--preload","--worker-tmp-dir","/dev/shm"] Cmd=["isso.run"]

即官方镜像以 gunicorn 4 worker 预加载模式承载 isso.run 应用,监听容器内 0.0.0.0:8080。

  • 拉取镜像(本文为重复执行复核;首次拉取会逐层输出下载进度):
docker pull ghcr.io/isso-comments/isso:release

实测回显:

Status: Image is up to date for ghcr.io/isso-comments/isso:release

1.3 前置条件核查

  1. 确认 Docker 引擎与 Compose 插件版本:
docker version --format 'Server: {{.Server.Version}}'
docker compose version

实测回显:

Server: 29.9.2
Docker Compose version v5.6.1
  1. 复核宿主端口 8141、8142 未被占用(动工前必做):
ss -ltn | grep -E ':8141|:8142'

无输出即两端口均空闲;被占用时先定位占用进程,或在编排文件中同步替换为其他空闲端口(并同步修改演示页嵌入地址与配置白名单)。

  1. 规划独立子网。约定在 10.89.0.0/16 内选槽位,避开宿主局域网段(192.168.0.0/16)与运营商级 NAT 段(100.64.0.0/16)。先列出已被既有自定义网络占用的槽位:
docker network ls --format '{{.Name}}' | xargs -I{} sh -c \
  "docker network inspect {} --format '{{.Name}}: {{range .IPAM.Config}}{{.Subnet}}{{end}}'" | grep '10\.89\.' | sort

实测回显(本栈创建后的复核输出;动工前该列表中尚无 10.89.71.0/24,规划时按同法确认目标槽位空闲,被占用则顺延另选):

baikal-0101-stack_baikal-net: 10.89.62.0/24
isso-0.14.0-net: 10.89.71.0/24
librebooking-v710-stack_librebooking-net: 10.89.63.0/24
plakar-net: 10.89.65.0/24

二、编写 Compose 编排文件

2.1 目录结构与配置文件

创建部署目录与三个子目录:

mkdir -p /opt/data/quzuo/tmp/isso/config /opt/data/quzuo/tmp/isso/db /opt/data/quzuo/tmp/isso/page

部署目录最终结构(全部落在宿主机,容器仅挂载引用):

路径用途挂载点
config/isso.cfgIsso 主配置文件/config(只读)
db/SQLite 评论库目录(comments.db)/db(可写)
page/index.html演示宿主页/usr/share/nginx/html(只读)
compose.yamlCompose 编排文件—

config/isso.cfg 全文(本文实测所用最终配置,逐节参数含义见本系列配置参数详解篇):

[general]
dbpath = /db/comments.db
host =
    http://10.89.71.1:8142/
    http://127.0.0.1:8142/
    http://localhost:8142/
max-age = 15m
notify = stdout, smtp
reply-notifications = true
gravatar = true
latest-enabled = true

[server]
listen = http://0.0.0.0:8080/
trusted-proxies =
    10.89.71.1
samesite = Lax

[admin]
enabled = true
password = q1w2e3r4

[moderation]
enabled = true
approve-if-email-previously-approved = true
purge-after = 30d

[guard]
enabled = true
ratelimit = 4
direct-reply = 3
reply-to-self = true
require-author = false
require-email = false

[smtp]
host = 10.89.71.1
port = 2525
security = none
username =
password =
to = admin@example.com
from = isso@example.com
timeout = 10

[markup]
renderer = mistune
allowed-elements = img
allowed-attributes = src, alt

[hash]
salt = isso-test-salt-2026
algorithm = pbkdf2

[rss]
base = http://127.0.0.1:8142/
limit = 100

[general] host 白名单三个条目的分工:http://10.89.71.1:8142/ 是容器网络的网关地址,即宿主机在 isso-net 中的身份——实测踩坑:当演示页直接跑在宿主机上时,Isso 服务端建线程要按白名单抓取宿主页,此时必须写网关地址(容器内可达),写宿主局域网 IP 会不可达;http://127.0.0.1:8142/ 与 http://localhost:8142/ 服务宿主本机直接访问演示页的场景(本部署演示页由 demo-page 服务提供,宿主经 8142 映射端口访问,命中的就是这两个条目)。

page/index.html 全文(演示宿主页):

<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="utf-8">
<title>Isso 演示宿主页</title>
</head>
<body>
<h1>Isso 演示文章:自托管评论系统实测</h1>
<p>这是一篇用于验证 Isso 评论服务的演示宿主页。</p>
<script data-isso="//127.0.0.1:8141/"
        src="//127.0.0.1:8141/js/embed.min.js"></script>
<section id="isso-thread" data-title="Isso 演示文章:自托管评论系统实测"></section>
</body>
</html>

2.2 编排文件全文

在部署目录创建 compose.yaml:

name: isso-0.14.0-stack

services:
  isso:
    image: ghcr.io/isso-comments/isso:release
    container_name: isso-0.14.0
    restart: unless-stopped
    ports:
      - "8141:8080"
    volumes:
      - ./config:/config:ro
      - ./db:/db
    networks:
      isso-net:
        ipv4_address: 10.89.71.10

  demo-page:
    image: nginx:alpine
    container_name: isso-demo-page
    restart: unless-stopped
    ports:
      - "8142:80"
    volumes:
      - ./page:/usr/share/nginx/html:ro
    networks:
      isso-net:
        ipv4_address: 10.89.71.11

networks:
  isso-net:
    name: isso-0.14.0-net
    driver: bridge
    ipam:
      config:
        - subnet: 10.89.71.0/24

2.3 编排设计逐项解说

  • 显式项目名 name: isso-0.14.0-stack:Compose 项目名必须显式指定。多套自托管栈共存于同一台服务器时,同名项目的 down 操作会误删其他组件的栈——这是多服务共存的硬性纪律。
  • 双服务固定 IP:isso 服务 10.89.71.10、演示页 10.89.71.11,子网显式 10.89.71.0/24。固定 IP 让 [general] host 白名单、[server] trusted-proxies、[smtp] host 等配置项都有稳定明确的对端地址。
  • 配置只读挂载 ./config:/config:ro:官方镜像通过内置环境变量 ISSO_SETTINGS=/config/isso.cfg 定位主配置(见第六节),配置文件设为只读可防止容器内进程意外改写;修改配置只需编辑宿主文件后 docker compose restart。
  • 数据持久化 ./db:/db:与配置中 [general] dbpath = /db/comments.db 对应,全部评论数据落在宿主机 db/ 目录的 SQLite 单文件中(见第五节)。
  • restart: unless-stopped:宿主机重启后自动拉起,人工停止后不随 Docker 守护进程复活。
  • 演示页独立成服务:嵌入脚本 data-isso 指向 //127.0.0.1:8141/(Isso 的宿主映射端口),页面与评论服务同网络但宿主入口端口分离,验证环境贴近真实「博客页面 + 评论服务」的拓扑。

三、启动与首次验证

3.1 启动编排栈

cd /opt/data/quzuo/tmp/isso && docker compose up -d

实测回显:

 Container isso-0.14.0 Running
 Container isso-demo-page Creating
 Container isso-demo-page Created
 Container isso-demo-page Starting
 Container isso-demo-page Started

(本文环境为增量执行:isso 服务此前已在运行故显示 Running;全新部署时两个服务都会依次经历 Creating → Created → Starting → Started。)

3.2 运行状态与端口监听

docker compose ps

实测回显(CREATED 列为执行时点的相对时间):

NAME             IMAGE                                COMMAND                  SERVICE     CREATED        STATUS                  PORTS
isso-0.14.0      ghcr.io/isso-comments/isso:release   "/isso/bin/gunicorn …"   isso        3 hours ago    Up 10 minutes           0.0.0.0:8141->8080/tcp, [::]:8141->8080/tcp
isso-demo-page   nginx:alpine                         "/docker-entrypoint.…"   demo-page   1 second ago   Up Less than a second   0.0.0.0:8142->80/tcp, [::]:8142->80/tcp
ss -ltn | grep -E ':8141|:8142'

实测回显:

LISTEN 0      4096         0.0.0.0:8141       0.0.0.0:*
LISTEN 0      4096         0.0.0.0:8142       0.0.0.0:*
LISTEN 0      4096            [::]:8141          [::]:*
LISTEN 0      4096            [::]:8142          [::]:*

3.3 启动日志解读

docker logs isso-0.14.0 2>&1 | head -12

实测回显:

2026-10-30 08:53:05,725:INFO: Loading Mistune with plugins: ['strikethrough', 'subscript', 'superscript'] and parameters: ['escape', 'hard_wrap']
2026-10-30 08:53:05,725:INFO: Using Mistune as Markdown rendering engine
2026-10-30 08:53:05,729:INFO: Using database at '/db/comments.db'
2026-10-30 08:53:05,735:WARNING: unable to connect to your website, Isso will probably not work correctly. Please make sure, Isso can reach your website via HTTP(S).
[2026-10-30 08:53:05 +0000] [1] [INFO] Starting gunicorn 25.2.0
[2026-10-30 08:53:05 +0000] [1] [INFO] Listening at: http://0.0.0.0:8080 (1)
[2026-10-30 08:53:05 +0000] [1] [INFO] Using worker: sync
[2026-10-30 08:53:05 +0000] [7] [INFO] Booting worker with pid: 7
[2026-10-30 08:53:05 +0000] [8] [INFO] Booting worker with pid: 8
[2026-10-30 08:53:05 +0000] [9] [INFO] Booting worker with pid: 9
[2026-10-30 08:53:05 +0000] [10] [INFO] Booting worker with pid: 10
[2026-10-30 08:53:06 +0000] [1] [INFO] Control socket listening at /root/.gunicorn/gunicorn.ctl

逐行含义:前两行确认 0.14.0 的默认 Markdown 渲染引擎为 Mistune(含删除线/上下标插件与 escape 参数);第三行确认数据库落在挂载目录;WARNING 行是启动时对 [general] host 白名单逐项发起 HTTP HEAD 探测、全部不可达时输出(源码 isso/__init__.py)——本文截取日志时演示页尚未就绪,该告警不阻断 API 服务,服务端建线程抓页按需进行;其余为 gunicorn 25.2.0 四个 sync worker 的启动序列。

3.4 服务探活

Isso 提供两个免认证的公开信息端点:

curl -s http://127.0.0.1:8141/info
curl -s http://127.0.0.1:8141/config

实测回显:

{"version": "0.14.0", "host": "http://127.0.0.1:8141", "origin": "http://10.89.71.1:8142", "moderation": true}
{"config": {"reply-to-self": true, "require-email": false, "require-author": false, "reply-notifications": true, "gravatar": true, "avatar": false, "feed": true}}

/info 的 version: "0.14.0" 即版本确认(镜像 :release 标签内容与上游最新稳定版一致);moderation: true 表示审核开启。/config 返回嵌入页可读的公开开关集合,与 isso.cfg 中对应配置项一一映射(详见本系列配置参数详解篇)。

四、嵌入演示页与评论链路验证

4.1 页面嵌入三要素

演示页 index.html 中与 Isso 相关的共三处:

  1. <script data-isso="//127.0.0.1:8141/" src="//127.0.0.1:8141/js/embed.min.js">:data-isso 告诉客户端脚本评论服务的地址;embed.min.js 由 isso 服务自身静态托管;
  2. <section id="isso-thread">:评论区的挂载点,id 固定为 isso-thread;
  3. data-title="Isso 演示文章:自托管评论系统实测":线程标题来源——客户端以该值作为 title 参数提交(源码 js/app/isso.js)。若不提供,服务端会按请求来源抓取页面标题建线程,此时要求 [general] host 白名单可从服务端侧访问到页面(排障见第十节第 2 条)。

静态资源与页面可达性验证:

curl -s -o /dev/null -w 'GET http://127.0.0.1:8142/index.html -> %{http_code}\n' http://127.0.0.1:8142/index.html
curl -s -o /dev/null -w 'GET http://127.0.0.1:8141/js/embed.min.js -> %{http_code}\n' http://127.0.0.1:8141/js/embed.min.js

实测回显:

GET http://127.0.0.1:8142/index.html -> 200
GET http://127.0.0.1:8141/js/embed.min.js -> 200

演示页服务的访问日志(来源 10.89.71.1 即宿主机在桥接网络中的网关地址):

docker logs isso-demo-page 2>&1 | grep 'GET /index.html' | tail -3

实测回显:

10.89.71.1 - - [30/Oct/2026:11:52:17 +0000] "GET /index.html HTTP/1.1" 200 454 "-" "curl/8.18.0" "-"

4.2 评论提交冒烟与反垃圾拦截实测

模拟浏览器提交(POST JSON 到 /new,uri 与 title 走查询串,Origin/Referer 头命中白名单)。第一次以「直评」形式提交,返回 403:

curl -s -i -X POST 'http://127.0.0.1:8141/new?uri=%2Findex.html&title=Isso%20%E6%BC%94%E7%A4%BA%E6%96%87%E7%AB%A0%EF%BC%9A%E8%87%AA%E6%89%98%E7%AE%A1%E8%AF%84%E8%AE%BA%E7%B3%BB%E7%BB%9F%E5%AE%9E%E6%B5%8B' \
  -H 'Origin: http://127.0.0.1:8142' -H 'Referer: http://127.0.0.1:8142/index.html' \
  -H 'Content-Type: application/json' \
  -d '{"text":"部署验收冒烟评论:Compose 双服务栈启动正常,评论链路畅通。","author":"周八","email":"zhouba@example.com"}'

实测回显:

HTTP/1.1 403 FORBIDDEN
Server: gunicorn
Date: Fri, 30 Oct 2026 12:01:23 GMT
Connection: close
Content-Type: text/html; charset=utf-8
Content-Length: 120
Access-Control-Allow-Origin: http://127.0.0.1:8142
Access-Control-Allow-Credentials: true
Access-Control-Allow-Methods: HEAD, GET, POST, PUT, DELETE
Access-Control-Allow-Headers: Origin, Referer, Content-Type
Access-Control-Expose-Headers: X-Set-Cookie, Date

<!doctype html>
<html lang=en>
<title>403 Forbidden</title>
<h1>Forbidden</h1>
<p>3 direct responses to /index.html</p>

3 direct responses to /index.html 是 guard 反垃圾模块的 direct-reply 限流拦截:同一来源(按匿名化后的 IP 网段聚合)对同一线程的直评累计已达配置上限 3。本文环境该来源配额已被此前的实测评论占满,恰好实证了拦截路径;全新部署环境首次直评不会被拦。改以「回复」形式提交(parent 指向一条既有评论 id 25,回复不受 direct-reply 计数约束):

curl -s -i -X POST 'http://127.0.0.1:8141/new?uri=%2Findex.html&title=Isso%20%E6%BC%94%E7%A4%BA%E6%96%87%E7%AB%A0%EF%BC%9A%E8%87%AA%E6%89%98%E7%AE%A1%E8%AF%84%E8%AE%BA%E7%B3%BB%E7%BB%9F%E5%AE%9E%E6%B5%8B' \
  -H 'Origin: http://127.0.0.1:8142' -H 'Referer: http://127.0.0.1:8142/index.html' \
  -H 'Content-Type: application/json' \
  -d '{"text":"部署验收冒烟回复:审核通过后对外可见。","author":"周八","email":"zhouba@example.com","parent":25}'

实测回显:

HTTP/1.1 202 ACCEPTED
Server: gunicorn
Date: Fri, 30 Oct 2026 12:03:00 GMT
Connection: close
Content-Type: application/json
Content-Length: 436
Set-Cookie: 26=WzI2LCI1NjYzMDdiZjZmZjk4MzJhYTM1NWU5MWJiZGNiMmE2ZmViMmI1M2E2Il0.auSHdA.xgJp2MVDiFr3NaoGRSrhxBSw6JI; Expires=Fri, 30 Oct 2026 12:18:00 GMT; Max-Age=900; Path=/; SameSite=Lax
X-Set-Cookie: isso-26=WzI2LCI1NjYzMDdiZjZmZjk4MzJhYTM1NWU5MWJiZGNiMmE2ZmViMmI1M2E2Il0.auSHdA.xgJp2MVDiFr3NaoGRSrhxBSw6JI; Expires=Fri, 30 Oct 2026 12:18:00 GMT; Max-Age=900; Path=/; SameSite=Lax
Access-Control-Allow-Origin: http://127.0.0.1:8142
Access-Control-Allow-Credentials: true
Access-Control-Allow-Methods: HEAD, GET, POST, PUT, DELETE
Access-Control-Allow-Headers: Origin, Referer, Content-Type
Access-Control-Expose-Headers: X-Set-Cookie, Date

{"id": 26, "parent": 25, "created": 1793361780.5222783, "modified": null, "mode": 2, "text": "<p>\u90e8\u7f72\u9a8c\u6536\u5192\u70df\u56de\u590d\uff1a\u5ba1\u6838\u901a\u8fc7\u540e\u5bf9\u5916\u53ef\u89c1\u3002</p>", "author": "\u5468\u516b", "website": null, "likes": 0, "dislikes": 0, "notification": null, "hash": "c1905c0f8037", "gravatar_image": "https://www.gravatar.com/avatar/9034ce632c0b6990cfdef207fbdb8149?d=identicon&s=55"}

三个关键信号:202 + mode: 2 表示评论已入库但处于待审核状态([moderation] enabled = true 的预期行为;若配置了「邮箱曾获通过自动放行」且该邮箱命中,则返回 201 + mode: 1 直接入库);响应体同时携带 Set-Cookie 与 X-Set-Cookie 两个头——后者交由嵌入页脚本写入 localStorage,是评论作者 15 分钟内编辑/删除自己评论的凭据([general] max-age = 15m);gravatar_image 字段由 [general] gravatar = true 开启。审核放行操作见本系列配置参数详解篇的审核闭环实测。

4.3 公开列表与计数

拉取公开评论列表(limit=1 仅返回 1 条顶层评论用于演示分页):

curl -s 'http://127.0.0.1:8141/?uri=%2Findex.html&limit=1'

实测回显:

{"id": null, "total_replies": 12, "hidden_replies": 8, "replies": [{"id": 5, "parent": null, "created": 1793359408.7815886, "modified": 1793359871.0028844, "mode": 1, "text": "<p>\u8fd9\u662f\u7b2c\u4e00\u6761\u5b9e\u6d4b\u8bc4\u8bba\uff1aIsso \u90e8\u7f72\u5b8c\u6210\uff0c\u8bc4\u8bba API \u5de5\u4f5c\u6b63\u5e38\u3002\uff08\u5df2\u7f16\u8f91\uff1a\u8865\u5145\u914d\u7f6e\u8def\u5f84\uff09</p>", "author": "\u5f20\u4e09", "website": null, "likes": 0, "dislikes": 0, "notification": null, "hash": "47b6b7a99262", "gravatar_image": "https://www.gravatar.com/avatar/f0c3acd8a5e2b954b76bfd774a667cf1?d=identicon&s=55", "total_replies": 0, "hidden_replies": 0, "replies": []}], "config": {"reply-to-self": true, "require-email": false, "require-author": false, "reply-notifications": true, "gravatar": true, "avatar": false, "feed": true}}

字段口径:total_replies 是该线程对外可见评论总数(mode 计数掩码只含已通过状态,待审评论不计入);hidden_replies 是本次响应未展示的顶层评论数(顶层可见 9 条,limit=1 返回 1 条,故为 8);首条评论的 modified 字段非空即「评论者 15 分钟内编辑自己评论」的留存痕迹。

页面侧计数接口(嵌入页的「N 条评论」角标取数):

curl -s -X POST http://127.0.0.1:8141/count -H 'Content-Type: application/json' -d '["/index.html"]'

实测回显(提交待审评论前后各执行一次,结果不变——待审评论既不进列表也不进计数):

[12]

五、数据持久化说明

宿主机 db/ 目录挂载到容器 /db,与配置 dbpath = /db/comments.db 对应,Isso 的全部落盘内容就是这一个 SQLite 文件:

ls -la /opt/data/quzuo/tmp/isso/db/

实测回显:

total 40
drwxr-xr-x 2 root root  4096 Oct 30 19:52 .
drwxr-xr-x 7 root root  4096 Oct 30 19:19 ..
-rw-r--r-- 1 root root 32768 Oct 30 19:52 comments.db

「Because comments are not Big Data」——官方对 SQLite 后端的定位在实测中也得到印证:十余条评论与线程元数据共 32KB。备份直接打包即可(无需停服,SQLite 单文件一致性在低并发写入下足够;严谨起见可在空闲期备份):

tar czf /opt/data/quzuo/tmp/isso/isso-comments-2026-10-30.tgz -C /opt/data/quzuo/tmp/isso db
ls -la /opt/data/quzuo/tmp/isso/*.tgz

实测回显:

-rw-r--r-- 1 root root 2600 Oct 30 20:01 /opt/data/quzuo/tmp/isso/isso-comments-2026-10-30.tgz

恢复 = 解包回 db/ 目录后 docker compose up -d;整站迁移 = 连同 config/、page/ 一起打包,在新机同构部署后替换目录即可。

六、环境变量说明

Isso 与「环境变量直配运行参数」的路线保持距离,只有一个关键的内置变量:

变量镜像内置值作用
ISSO_SETTINGS/config/isso.cfg指定主配置文件路径,优先级高于命令行 -c 参数
  • 该变量由官方镜像直接内置(docker inspect 实测):
docker inspect isso-0.14.0 --format '{{range .Config.Env}}{{println .}}{{end}}' | grep ISSO

实测回显:

ISSO_SETTINGS=/config/isso.cfg
  • 源码注释明确其优先级(isso/__init__.py:ISSO_SETTINGS env var takes precedence over -c flag);多站点分发模式(isso.dispatch 入口)则强制要求设置该变量。
  • 0.14.0 新增配置文件取值的环境变量替换能力(官方 CHANGES #1037,实现于 isso/config.py 的 IssoParser.get,对每个配置值做路径风格的变量展开),适合把口令类敏感项留存在宿主环境而非写入配置文件。
  • 除 ISSO_SETTINGS 外没有全量环境变量映射,运行参数一律以 isso.cfg 为准——这与部分竞品的「大写键映射配置树」环境变量风格不同,迁移评估时需留意。

七、配置参数速览

isso.cfg 共九个配置节,本文部署所用取值与作用速览(逐项实测行为见本系列配置参数详解篇):

配置节本文取值要点作用域
[general]数据库路径、host 白名单三来源、max-age 15m、notify stdout+smtp、gravatar、latest-enabled全局:存储、站点白名单、通知与扩展开关
[server]监听 8080、trusted-proxies 10.89.71.1、samesite LaxHTTP 监听与代理信任链
[admin]enabled + 口令 q1w2e3r4Web 管理后台 /admin
[moderation]enabled、曾获通过自动放行、purge-after 30d评论审核策略
[guard]ratelimit 4、direct-reply 3、reply-to-self true反垃圾限流(4.2 节已实测拦截)
[smtp]通知邮箱服务器与收发件人、security none邮件通知渠道
[markup]mistune 渲染器、白名单放行 imgMarkdown 渲染与元素白名单
[hash]salt、pbkdf2评论者邮箱匿名化指纹
[rss]base、limit 100评论 Atom Feed

八、等价 docker run 形态

不使用 Compose 时,以下命令与本文编排文件等价(供参考,未重复执行;镜像已内置 ISSO_SETTINGS,无需额外 -e 参数):

docker network create --driver bridge --subnet 10.89.71.0/24 isso-0.14.0-net

docker run -d --name isso-0.14.0 \
  --network isso-0.14.0-net --ip 10.89.71.10 \
  -p 8141:8080 \
  -v /opt/data/quzuo/tmp/isso/config:/config:ro \
  -v /opt/data/quzuo/tmp/isso/db:/db \
  --restart unless-stopped \
  ghcr.io/isso-comments/isso:release

docker run -d --name isso-demo-page \
  --network isso-0.14.0-net --ip 10.89.71.11 \
  -p 8142:80 \
  -v /opt/data/quzuo/tmp/isso/page:/usr/share/nginx/html:ro \
  --restart unless-stopped \
  nginx:alpine

九、常用运维命令

操作命令
查看栈状态docker compose ps(部署目录执行)
评论服务日志docker logs -f --tail 100 isso-0.14.0
演示页日志docker logs -f --tail 50 isso-demo-page
改配置后生效编辑 config/isso.cfg 后 docker compose restart
停止 / 启动docker compose down / docker compose up -d
数据备份tar czf isso-comments-2026-10-30.tgz -C /opt/data/quzuo/tmp/isso db
进入容器排查docker exec -it isso-0.14.0 sh
版本确认curl -s http://127.0.0.1:8141/info
升级版本docker compose pull && docker compose up -d(:release 随上游稳定版前滚;需要锁版本时以镜像摘要固定,升级前先做第五节数据备份)

十、常见问题

  1. 启动日志出现 WARNING: unable to connect to your website:启动时 Isso 会对 [general] host 白名单逐项发 HTTP HEAD 探测,全部不可达即告警(3.3 节实录)。演示页尚未就绪时该告警可忽略;若持续出现,检查白名单地址是否为「从 Isso 服务侧可达」的地址。
  2. 提交评论返回 400 Cannot create new thread: URI ... is not accessible and no title was provided:嵌入页未提供 data-title 且服务端按白名单抓取页面失败。两个修法:给 <section id="isso-thread"> 补 data-title(推荐,客户端始终带 title 提交);或确保白名单首项是服务端可达地址——实测踩坑:页面跑在宿主机时必须写网关地址 10.89.71.1:8142,写宿主局域网 IP 从容器内不可达,必然复现此错。
  3. 提交成功(202)但页面上看不到评论:[moderation] enabled = true 下新评论一律待审,需管理员在 /admin 后台放行;待审评论同时不进列表与计数(4.3 节实测)。注意 purge-after(本文 30d)会自动清理超期未审的待审评论——审核要及时。
  4. 浏览器控制台跨域报错:Isso 只向 [general] host 白名单内的来源返回 Access-Control-Allow-Origin 响应头(4.2 节回显可见响应头逐字回显了白名单来源 http://127.0.0.1:8142)。页面真实访问来源(协议 + 地址 + 端口)必须与白名单条目完全一致。
  5. 连发评论被 403 拦截:guard 反垃圾在起作用——ratelimit 限制同一来源每分钟评论数(本文 4 条),direct-reply 限制对同一线程的直评累计数(本文 3 条,4.2 节实测命中)。来源按匿名化后的 IP 网段聚合;经代理接入时需配置 [server] trusted-proxies 并让代理传递 X-Forwarded-For,否则所有用户会共享代理出口的配额。
  6. 端口冲突:docker compose up 报端口占用时用 ss -ltnp | grep 端口号 定位;换宿主端口后同步修改演示页 data-isso 指向与 host 白名单条目。
  7. 管理后台口令遗失:修改 isso.cfg 的 [admin] password 后 docker compose restart 即生效,无其他恢复路径。

参考链接

  1. Isso 官网:https://isso-comments.de
  2. 官方文档:https://isso-comments.de/docs/
  3. GitHub 仓库:https://github.com/posativ/isso
  4. Releases 页(0.14.0 版本查证处):https://github.com/posativ/isso/releases
  5. 官方镜像说明(CHANGES #970 镜像 tag 语义):https://github.com/posativ/isso/blob/master/CHANGES.rst