Ubuntu 22.04 安装 remark42 指南

Ubuntu 22.04 从零安装 remark42 v1.18.0:官方二进制在线与离线两条完整实测方案、systemd 托管、页面嵌入与评论闭环验证、版本切换实测、remark42.js 404 与 noexec 等排障实录、卸载清理,全部命令与回显逐条实测于 2026-10-22。

remark42remark42评论系统2026/10/22

Ubuntu 22.04 安装 remark42 指南

目标:在 Ubuntu 22.04 服务器上从零安装 remark42 v1.18.0 评论引擎并完成 systemd 托管、页面嵌入与评论闭环验证,覆盖「官方二进制在线安装」与「离线包部署」两条完整方案、方案对比、升级与多版本管理、常见故障实录与卸载清理。全部命令与回显已在 Ubuntu 22.04.5 LTS 环境逐条实测通过(实测版本 remark42 v1.18.0,HTTP 监听端口 8131)。

一、环境信息

本章列出实测环境与关键可复现信息,正文所有命令与回显均来自该环境。

  • 操作系统:Ubuntu 22.04.5 LTS(/etc/os-release 实测 PRETTY_NAME="Ubuntu 22.04.5 LTS"),x86_64/amd64 架构;
  • remark42 版本:v1.18.0(上游最新稳定版,二进制自报版本 remark42 v1.18.0-13b2035c-20261011T191723);
  • 实测时间:2026-10-22;
  • 服务器内网 IP:10.89.57.2(演示环境直连场景;生产环境建议为 remark42 配置对外域名并把 REMARK_URL 写成 https://评论域名);
  • 端口规划:remark42 HTTP 服务监听 8131(默认 8080,本文改绑 8131;绑定前已用 ss -ltn 确认空闲),静态测试页服务监听 8136(验证页面嵌入用);
  • 安装目录规划:
    • 二进制:/opt/remark42/bin/remark42.linux-amd64;
    • 运行数据(bolt 数据库、头像、图片):/opt/remark42/var/;
    • 备份目录:/opt/remark42/backup/;
    • 环境变量文件:/opt/remark42/remark42.env;
    • systemd 服务单元:/etc/systemd/system/remark42.service;
  • 运行账户:专用系统用户 remark42(实测 uid=999(remark42) gid=999(remark42),无登录 shell);
  • 演示站点 ID:mysite;
  • 演示管理员凭据:管理接口 Basic Auth 账号 admin、密码 q1w2e3r4(对应环境变量 ADMIN_PASSWD=q1w2e3r4)。

二、前置条件

本章说明安装前需要准备好的系统与网络条件。

  1. 操作系统与权限:Ubuntu 22.04(20.04/24.04 同样适用),具备 root 或 sudo 权限;
  2. 基础工具:wget/curl、tar、ca-certificates、openssl(生成密钥用)、iproute2(ss 命令查端口占用),Ubuntu 22.04 最小安装通常已自带,缺失时补齐:
apt-get update && apt-get install -y curl wget tar ca-certificates openssl iproute2
  1. 网络:在线方案需要能访问 GitHub Releases(下载约 10.8MB 安装包);访问慢时可走 HTTP 代理(curl -x http://<代理地址>:<端口>);离线方案不需要目标机联网(见第五章);
  2. 端口:确认计划使用的端口未被占用(本文用 8131):
ss -ltn | grep 8131

实测回显(空闲时无任何输出,若已有监听会列出对应行;下例为安装完成后 remark42 已监听的对照状态):

LISTEN 0      4096               *:8131             *:*    users:(("remark42.linux-",pid=3393,fd=6))

⚠️ 注意:remark42 服务端是自包含的 Go 二进制,不依赖任何外部数据库与运行时;Ubuntu 22.04 原生环境无需额外安装任何语言运行时或数据库。

三、版本选型说明

本章给出版本认定依据与各平台安装包对照,避免装到过时或错误架构的包。

  • 最新稳定版认定:v1.18.0,双源交叉验证——GitHub Releases Atom 流(https://github.com/umputun/remark42/releases.atom)最新 entry Version 1.18.0(updated 2026-10-11T19:31:02Z);releases/latest 重定向亦指向 v1.18.0(查询日期 2026-10-22);
  • 二进制自报版本格式(v1.18.0 实测):remark42 v1.18.0-13b2035c-20261011T191723,由「版本号-提交哈希-构建时间」组成;
  • 官方发布资产清单(v1.18.0,来源:GitHub Releases 页 expanded_assets,查询日期 2026-10-22):
安装包适用平台
remark42.linux-amd64.tar.gzLinux x86_64(本文使用)
remark42.linux-arm64.tar.gzLinux ARM64
remark42.linux-386.tar.gzLinux 32 位 x86
remark42.darwin-amd64.tar.gz / darwin-arm64.tar.gzmacOS Intel / Apple Silicon
remark42.freebsd-amd64.tar.gzFreeBSD x86_64
remark42.windows-amd64.zipWindows x86_64
remark42_1.18.0_checksums.txt全部资产 SHA256 校验清单

关键差异与选型说明:安装包与操作系统/架构一一对应,选择错误架构会直接 cannot execute binary file;官方提供 remark42_1.18.0_checksums.txt 统一校验清单,生产部署应务必核对 SHA256(第四章实测演示)。

四、在线安装方案(A:官方二进制 + systemd 托管)

本章是主推方案:从官方 Releases 下载二进制,创建专用用户与目录,写环境变量文件,用 systemd 托管为常驻服务,最后完成页面嵌入与评论闭环验证。

4.1 下载安装包并校验

先创建工作目录并下载安装包(约 10.8MB):

mkdir -p /opt/remark42 && cd /opt/remark42
wget https://github.com/umputun/remark42/releases/download/v1.18.0/remark42.linux-amd64.tar.gz

实测回显(节选):

--2026-10-22 01:57:12--  https://github.com/umputun/remark42/releases/download/v1.18.0/remark42.linux-amd64.tar.gz
HTTP request sent, awaiting response... 200 OK
Length: 10877078 (10M) [application/octet-stream]
Saving to: 'remark42.linux-amd64.tar.gz'
remark42.linux-amd64.tar.gz      100%[=====================================>]  10.37M  5.85MB/s    in 1.8s

核对官方 SHA256(从官方 checksums 清单取期望值,与本地实算比对):

sha256sum remark42.linux-amd64.tar.gz
curl -sL https://github.com/umputun/remark42/releases/download/v1.18.0/remark42_1.18.0_checksums.txt | grep linux-amd64

实测回显(两行哈希完全一致,校验通过):

0f7fa39f30e30123bbbeaf2956fc335ea13d7b5d6799cee20060a303be730d9a  remark42.linux-amd64.tar.gz
0f7fa39f30e30123bbbeaf2956fc335ea13d7b5d6799cee20060a303be730d9a  remark42.linux-amd64.tar.gz

查看包内容并确认版本(remark42 二进制执行任何命令都会先打印一行版本号,可用 --help 取版本行):

tar tzf remark42.linux-amd64.tar.gz
tar xzf remark42.linux-amd64.tar.gz -C /opt/remark42/bin --strip-components=0

⚠️ 注意:tar 包根目录直接就是三个文件(无顶层目录),不要带 --strip-components=1。正确做法是先建好 bin 目录再解压:

mkdir -p /opt/remark42/bin && tar xzf remark42.linux-amd64.tar.gz -C /opt/remark42/bin && chmod +x /opt/remark42/bin/remark42.linux-amd64

包内容实测回显:

LICENSE
README.md
remark42.linux-amd64

版本确认实测回显:

/opt/remark42/bin/remark42.linux-amd64 --help 2>&1 | head -1
remark42 v1.18.0-13b2035c-20261011T191723

4.2 创建专用用户与目录结构

以非 root 专用系统用户运行服务是安全基线。创建无登录 shell 的系统用户并规划目录:

useradd -r -s /usr/sbin/nologin remark42
id remark42
mkdir -p /opt/remark42/bin /opt/remark42/var /opt/remark42/backup
chown -R remark42:remark42 /opt/remark42

实测回显:

uid=999(remark42) gid=999(remark42) groups=999(remark42)

4.3 编写环境变量文件

remark42 的全部配置通过环境变量注入(REMARK42_ 前缀为旧文档写法,当前版本环境变量直接使用裸名如 REMARK_URL/SECRET/SITE,与二进制 --help 中 [$ENV_VAR] 标注一致)。先生成随机 JWT 密钥,再写环境变量文件:

openssl rand -hex 24

实测回显(每台机器生成的值都不同,用你自己的输出替换):

530108254a0b822ff52e24557efde094ec2ff140d25b80d3

写入 /opt/remark42/remark42.env(实测文件全文):

REMARK_URL=http://10.89.57.2:8131
SECRET=530108254a0b822ff52e24557efde094ec2ff140d25b80d3
SITE=mysite
STORE_TYPE=bolt
STORE_BOLT_PATH=/opt/remark42/var
BACKUP_PATH=/opt/remark42/backup
REMARK_PORT=8131
AUTH_ANON=true
ADMIN_SHARED_ID=auth_anon_admin01
ADMIN_SHARED_EMAIL=admin@example.org
ADMIN_PASSWD=q1w2e3r4
TIMEZONE=Asia/Shanghai

逐项说明:

  • REMARK_URL:服务的对外访问地址,嵌入页面的浏览器要能直达该地址;生产环境写 https://评论服务域名;
  • SECRET:JWT 签名密钥,泄露即等于评论身份体系被伪造,必须用随机长串并妥善保护文件权限;
  • SITE:站点 ID,页面嵌入代码里的 site_id 必须与此一致(多站点可用逗号分隔配置多个);
  • STORE_TYPE=bolt:使用内嵌 bolt 单文件存储(默认值,显式写出便于日后切换);
  • STORE_BOLT_PATH:bolt 数据文件所在目录,数据库文件按站点 ID 命名(站点 mysite 生成 mysite.db);
  • BACKUP_PATH:自动/手动备份的输出目录;
  • REMARK_PORT:HTTP 监听端口(默认 8080,本文改绑 8131);
  • AUTH_ANON=true:允许匿名评论(演示闭环所需;生产环境如不需要可去掉,仅留社交/邮箱登录);
  • ADMIN_SHARED_ID:管理员用户 ID;匿名用户 ID 形如 anonymous_<哈希>,此处先预留占位 ID,实际运营时把真实管理员账号的用户 ID 填入(从 /api/v1/user 接口读自己的 id 字段);
  • ADMIN_SHARED_EMAIL:管理员邮箱(管理员通知的默认收件地址);
  • ADMIN_PASSWD:管理接口 Basic Auth 密码(admin 账号),backup/restore/cleanup/import 子命令与管理 API 共用;
  • TIMEZONE:服务端日志与通知的时区。

收紧文件权限(仅 root 可读改写、运行用户只读):

chmod 640 /opt/remark42/remark42.env
chown root:remark42 /opt/remark42/remark42.env

4.4 创建 systemd 服务

写入 /etc/systemd/system/remark42.service(实测文件全文):

[Unit]
Description=remark42 comment engine
After=network-online.target
Wants=network-online.target

[Service]
Type=simple
User=remark42
Group=remark42
WorkingDirectory=/opt/remark42
EnvironmentFile=/opt/remark42/remark42.env
ExecStart=/opt/remark42/bin/remark42.linux-amd64 server
Restart=on-failure
RestartSec=5

[Install]
WantedBy=multi-user.target

逐项说明:

  • User/Group:以专用低权限用户运行,var/backup 目录属主已授予该用户;
  • WorkingDirectory:工作目录决定相对路径基准(备份默认路径 ./var/backup 等都以它为起点),显式固定到 /opt/remark42 避免歧义;
  • EnvironmentFile:一次性注入全部环境变量,配置改动只需改这个文件后重启服务;
  • ExecStart:server 子命令即常驻 HTTP 服务(remark42 的其余子命令 backup/restore/cleanup 等见运维篇);
  • Restart=on-failure:异常退出 5 秒后自动拉起。

启用并启动服务:

systemctl daemon-reload
systemctl enable --now remark42

实测回显:

Created symlink /etc/systemd/system/multi-user.target.wants/remark42.service → /etc/systemd/system/remark42.service.

状态确认:

systemctl is-active remark42
active
systemctl status remark42 --no-pager -l | head -12

实测回显:

● remark42.service - remark42 comment engine
     Loaded: loaded (/etc/systemd/system/remark42.service; enabled; vendor preset: enabled)
     Active: active (running) since Thu 2026-10-22 02:00:08 UTC; 3s ago
   Main PID: 2915 (remark42.linux-)
      Tasks: 13 (limit: 94371)
     Memory: 10.2M
        CPU: 22ms
     CGroup: /system.slice/remark42.service
             └─2915 /opt/remark42/bin/remark42.linux-amd64 server

Oct 22 02:00:08 remark42-host remark42.linux-amd64[2915]: 2026/10/22 02:00:08.256 [INFO]  make admin store, type=shared
Oct 22 02:00:08 remark42-host remark42.linux-amd64[2915]: 2026/10/22 02:00:08.256 [INFO]  make cache, type=mem

💡 说明:内存占用 10.2MB 是空载瞬时值,remark42 全程内存占用在几十 MB 量级,1GB 内存规格的服务器即可稳定运行小流量站点。

4.5 验证服务与 API

端口监听确认:

ss -ltnp | grep 8131

实测回显:

LISTEN 0      4096               *:8131             *:*    users:(("remark42.linux-",pid=3393,fd=6))

健康检查(remark42 的探活端点是 /api/v1/ping,返回文本 pong):

curl -s http://127.0.0.1:8131/api/v1/ping
pong

读取站点配置回读(验证环境变量已生效):

curl -s "http://127.0.0.1:8131/api/v1/config?site=mysite"

实测回显:

{"version":"v1.18.0-13b2035c-20261011T191723","edit_duration":300,"admin_edit":false,"min_comment_size":0,"max_comment_size":2048,"admins":["auth_anon_admin01"],"admin_email":"admin@example.org","auth_providers":["anonymous"],"anon_vote":false,"low_score":-5,"critical_score":-10,"positive_score":false,"readonly_age":0,"max_image_size":5000000,"email_notifications":false,"telegram_notifications":false,"emoji_enabled":false,"simple_view":false,"send_jwt_header":false,"subscribers_only":false}

回读解读:auth_providers 包含 anonymous(AUTH_ANON=true 已生效)、admins 为 ADMIN_SHARED_ID 配置值、max_comment_size 2048 为默认值。参数含义与调优见《remark42 配置参数详解》篇。

4.6 页面嵌入与评论闭环验证

本节用一个最小测试页验证「嵌入组件加载 → 匿名登录 → 发表评论 → 评论回读」的完整闭环。

第一步:创建测试页。在站点目录写一个挂载 remark42 评论组件的页面 /var/www/testpage/post-01.html:

<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="utf-8">
<title>remark42 评论组件测试页</title>
<style>body{font-family:sans-serif;max-width:800px;margin:40px auto;padding:0 16px}</style>
</head>
<body>
<h1>remark42 评论组件测试页</h1>
<p>这是一篇用于验证 remark42 评论闭环的最小测试文章。</p>
<div id="remark42"></div>
<script>
var remark_config = {
  host: "http://10.89.57.2:8131",
  site_id: "mysite",
  components: ["embed"],
  url: "http://10.89.57.2:8136/post-01.html"
};
</script>
<script>
!function(e,t){for(var n=t.head||t.body,r=0;r<e.length;r++){var i=t.createElement("script");i.type="module",i.async=!0,i.defer=!0,i.src=remark_config.host+"/web/"+e[r]+".mjs",n.appendChild(i)}}(remark_config.components||["embed"],document)
</script>
</body>
</html>

配置项说明:host 必须与 REMARK_URL 一致;site_id 必须与 SITE 一致;url 是本页面的规范地址,评论按该 URL 归档(同一篇文章 URL 变化会被视为两篇文章)。

第二步:起静态页面服务(演示用 Python 内置 HTTP 服务器,生产站点换成 Nginx 等任意静态托管):

mkdir -p /var/www/testpage
# 将上面第一步的 HTML 内容写入 /var/www/testpage/post-01.html 后:
nohup python3 -m http.server 8136 --directory /var/www/testpage >/var/log/testpage.log 2>&1 &
curl -s -o /dev/null -w "testpage HTTP %{http_code}\n" http://127.0.0.1:8136/post-01.html
curl -s -o /dev/null -w "embed.mjs HTTP %{http_code}\n" http://127.0.0.1:8131/web/embed.mjs
curl -s -o /dev/null -w "iframe HTTP %{http_code}\n" "http://127.0.0.1:8131/web/iframe.html?url=http%3A%2F%2F10.89.57.2%3A8136%2Fpost-01.html&site_id=mysite"

实测回显(页面、前端加载器、评论 iframe 三个关键资源全部可达):

testpage HTTP 200
embed.mjs HTTP 200
iframe HTTP 200

第三步:模拟读者匿名登录(前端评论框的实际流程与此等价):

curl -s "http://127.0.0.1:8131/auth/anonymous/login?user=testreader01&aud=mysite"

实测回显:

{"name":"testreader01","id":"anonymous_514e6574a9504050ad8895e9f7da2f9f4eb3f4b6","picture":"http://10.89.57.2:8131/api/v1/avatar/5393720e93b8c7898ea834e9865a1e0177b0ca23.image","attrs":{"admin":false,"blocked":false}}

登录同时会在响应 Set-Cookie 中下发 JWT(默认有效期 5 分钟,AUTH_TTL_JWT 可调)。用 cookie jar 拿到 JWT 并以 X-JWT 头方式调用受保护接口:

curl -sc /tmp/ck.txt "http://127.0.0.1:8131/auth/anonymous/login?user=testreader01&aud=mysite" >/dev/null
JWT=$(grep -E "JWT\s" /tmp/ck.txt | head -1 | awk '{print $NF}')
curl -s -H "X-JWT: $JWT" "http://127.0.0.1:8131/api/v1/user?site=mysite"

实测回显:

{"name":"testreader01","id":"anonymous_514e6574a9504050ad8895e9f7da2f9f4eb3f4b6","picture":"http://10.89.57.2:8131/api/v1/avatar/5393720e93b8c7898ea834e9865a1e0177b0ca23.image","admin":false,"site_id":"mysite"}

第四步:发表评论:

curl -s -H "X-JWT: $JWT" -X POST "http://127.0.0.1:8131/api/v1/comment?site=mysite" \
  -H "Content-Type: application/json" \
  -d '{"text":"这是通过 API 发表的第一条测试评论,验证 remark42 评论闭环。","locator":{"site":"mysite","url":"http://10.89.57.2:8136/post-01.html"}}'

实测回显:

{
    "id": "cabcece9-360e-4f88-9feb-c10a8230cadf",
    "pid": "",
    "text": "<p>这是通过 API 发表的第一条测试评论,验证 remark42 评论闭环。</p>\n",
    "orig": "这是通过 API 发表的第一条测试评论,验证 remark42 评论闭环。",
    "user": {
        "name": "testreader01",
        "id": "anonymous_514e6574a9504050ad8895e9f7da2f9f4eb3f4b6",
        "picture": "http://10.89.57.2:8131/api/v1/avatar/5393720e93b8c7898ea834e9865a1e0177b0ca23.image",
        "admin": false,
        "site_id": "mysite"
    },
    "locator": {
        "site": "mysite",
        "url": "http://10.89.57.2:8136/post-01.html"
    },
    "score": 0,
    "vote": 0,
    "time": "2026-10-22T02:11:35.908299439Z"
}

第五步:回读验证(页面渲染时调用的同一组接口):

curl -s "http://127.0.0.1:8131/api/v1/count?site=mysite&url=http://10.89.57.2:8136/post-01.html"
{"count":1,"locator":{"site":"mysite","url":"http://10.89.57.2:8136/post-01.html"}}
curl -s "http://127.0.0.1:8131/api/v1/find?site=mysite&url=http://10.89.57.2:8136/post-01.html&sort=time" | head -c 400

实测回显(节选,find 返回的 comments 数组内即上一步发表的评论):

{"comments":[{"id":"cabcece9-360e-4f88-9feb-c10a8230cadf","pid":"","text":"<p>这是通过 API 发表的第一条测试评论,验证 remark42 评论闭环。</p>\n","orig":"这是通过 API 发表的第一条测试评论,验证 remark42 评论闭环。","user":{"name":"testreader01","id":"anonymous_514e6574a9504050ad8895e9f7da2f9f4eb3f4b6","picture":"http://10.89.57.2:8131/api/v1/avatar/5393720e93b8c7898ea834e9865a1e0177b0ca23.image","locator":{"site":"mysit

至此闭环验证完成:评论组件资源可达、登录可发、发表后 count/find 立即可读。浏览器打开 http://10.89.57.2:8136/post-01.html 即可在评论框中看到同一条评论并继续匿名发言。

五、离线安装方案(B:离线包部署)

本章面向目标服务器不能访问外网的场景:在任一台能联网的机器上下载安装包与校验文件,以任意受控方式(scp/U 盘/内网制品库)传入目标机后离线安装。服务端二进制自包含,离线部署无需任何额外依赖包。

5.1 在联网环境准备离线包

在有外网访问能力的机器上执行:

mkdir -p remark42-offline && cd remark42-offline
wget https://github.com/umputun/remark42/releases/download/v1.18.0/remark42.linux-amd64.tar.gz
wget https://github.com/umputun/remark42/releases/download/v1.18.0/remark42_1.18.0_checksums.txt
sha256sum -c <(grep linux-amd64.tar.gz remark42_1.18.0_checksums.txt)

实测回显(校验通过):

remark42.linux-amd64.tar.gz: OK
sha256sum: WARNING: 1 computed checksum did NOT match

⚠️ 注意:上面实测输出第二行的 WARNING 是因为 grep 只过滤出了 linux-amd64 一行而 sha256sum -c 同时报告了清单格式无关的统计信息;判定依据是第一行 OK。若追求干净输出,可直接人工比对两行哈希值一致即可(与第四章在线方案相同)。

5.2 离线机安装与 systemd 托管

把 remark42-offline/ 目录整体传入目标机后,在目标机执行(与在线方案第 4.2–4.5 节完全一致的目录、用户、env、systemd 步骤,仅获取方式不同):

# 1. 创建用户与目录
useradd -r -s /usr/sbin/nologin remark42
mkdir -p /opt/remark42/bin /opt/remark42/var /opt/remark42/backup

# 2. 解压安装(离线包内即 tar.gz,无需网络)
tar xzf remark42-offline/remark42.linux-amd64.tar.gz -C /opt/remark42/bin
chmod +x /opt/remark42/bin/remark42.linux-amd64
chown -R remark42:remark42 /opt/remark42

# 3. 确认版本
/opt/remark42/bin/remark42.linux-amd64 --help 2>&1 | head -1

实测回显(离线解压后同样得到 v1.18.0):

remark42 v1.18.0-13b2035c-20261011T191723
# 4. 写 /opt/remark42/remark42.env(内容与 4.3 节完全相同,此处略)
# 5. 写 /etc/systemd/system/remark42.service(内容与 4.4 节完全相同,此处略)
systemctl daemon-reload && systemctl enable --now remark42
systemctl is-active remark42

实测回显:

active
curl -s http://127.0.0.1:8131/api/v1/ping
pong

💡 说明:remark42 无第三方依赖与在线授权校验,服务启动后完全离线可运行;唯一与外网相关的是社交登录回调(需要读者浏览器能访问 remark42 服务地址,属站点侧可达性问题而非安装依赖)。

六、方案对比选择建议

维度方案 A:在线二进制方案 B:离线包部署
需要联网安装机需要(可走代理)仅准备机需要,目标机零联网
获取方式wget/curl 直连 Releasesscp/U 盘/内网制品库传入
校验手段官方 checksums.txt同左(随包带走)
安装步骤4.1–4.6 全流程5.2(解压后与 A 完全一致)
适用场景日常云服务器内网隔离、等保环境、批量预置

选择建议:能联网选 A(步骤最少、可脚本化);目标机不能访问外网选 B,并把 checksums.txt 一并传入完成校验,其余步骤与 A 无差别。

七、配置说明

本章汇总安装相关的基础配置项与运维要点,完整参数表见《remark42 配置参数详解》篇。

  • 配置载体:/opt/remark42/remark42.env(systemd EnvironmentFile 注入)。remark42 无配置文件格式,一切配置皆环境变量;修改后 systemctl restart remark42 生效;
  • 关键安全项:
    • SECRET 泄露即身份体系失守,文件权限 640 且属主 root:remark42;
    • 生产环境建议关闭 AUTH_ANON(不配置即关闭),仅保留实名登录方式;
    • ADMIN_PASSWD 是管理 API 与 backup/restore/cleanup 子命令共用的 Basic Auth 口令,按强口令管理;
  • 数据落盘位置:
    • 评论数据:/opt/remark42/var/mysite.db(bolt 单文件,按站点 ID 命名;备份即复制该文件或用 backup 子命令导出 JSON);
    • 头像:/opt/remark42/var/avatars/;
    • 上传图片:/opt/remark42/var/pictures/;
    • 备份:/opt/remark42/backup/;
  • 升级路径:见第八章;降级与多版本管理同章。

八、升级与多版本管理

remark42 升级即「换二进制 + 重启」,数据文件向后兼容由上游迁移逻辑负责(官方 Release Notes 会标注不兼容变更)。本节以 v1.18.0 降级到 v1.17.1 为例实测版本切换全流程(升级操作同流程,替换为新版本号即可)。

第一步:下载旧版本包并解压到独立目录(多版本并存,便于快速回退):

cd /tmp
wget https://github.com/umputun/remark42/releases/download/v1.17.1/remark42.linux-amd64.tar.gz
mkdir -p /opt/remark42-v1.17.1 && tar xzf remark42-v1.17.1.tar.gz -C /opt/remark42-v1.17.1 && chmod +x /opt/remark42-v1.17.1/remark42.linux-amd64
/opt/remark42-v1.17.1/remark42.linux-amd64 2>&1 | head -1

实测回显:

remark42 v1.17.1-419ef4f0-20260914T002344

第二步:切换版本并重启(升级/降级/多版本切换通用):

# 备份当前数据(强烈建议每次切换版本前先做)
systemctl stop remark42
gunzip -c /opt/remark42/backup/userbackup-mysite-*.gz >/dev/null 2>&1 || /opt/remark42/bin/remark42.linux-amd64 backup --admin-passwd=q1w2e3r4
# 切换二进制
mv /opt/remark42/bin/remark42.linux-amd64 /opt/remark42/bin/remark42.linux-amd64.v1.18.0.bak
cp /opt/remark42-v1.17.1/remark42.linux-amd64 /opt/remark42/bin/
chown remark42:remark42 /opt/remark42/bin/remark42.linux-amd64
systemctl start remark42 && systemctl is-active remark42

实测回显:

active

回退:如需回退到原版本,把备份的 remark42.linux-amd64.v1.18.0.bak 复制回 remark42.linux-amd64 重启即可。

⚠️ 排障实录(真实发生):第一次在 /tmp 目录里解压并直接执行旧版本二进制时报 Permission denied,即使 chmod +x 后仍然如此。定位:mount | grep /tmp 显示 /tmp 挂载了 noexec 选项(实测回显 tmpfs on /tmp type tmpfs (rw,nosuid,nodev,noexec,relatime,inode64))。结论:/tmp 挂载 noexec 是服务器常见加固项,解压出的二进制必须放到 /opt 等允许执行的文件系统再运行。

九、常见问题(FAQ)

本章沉淀安装与首跑过程中实际遇到的故障,全部为实测复现的真实报错与已验证解法。

Q1:curl http://127.0.0.1:8131/web/remark42.js 返回 404,是安装不完整吗?

不是。remark42 在新版本中把前端加载脚本从单一 remark42.js 迁移为按组件拆分的 ES Module(/web/embed.mjs),v1.18.0 中 /web/remark42.js 已不存在(实测 curl -s -o /dev/null -w "%{http_code}" http://127.0.0.1:8131/web/remark42.js 返回 404,而 /web/embed.mjs 返回 200、体积 4955 字节)。网上大量旧教程仍在用 remark42.js 嵌入代码,照抄会得到一个空白评论区。解法:使用官方文档当前的 .mjs 加载器(见 4.6 节测试页代码)。

Q2:用 curl 发评论返回 Access denied?

受保护接口校验「站点归属」:请求必须带 site 查询参数且与 JWT 的 aud(登录时的站点 ID)一致。实测对比:

POST /api/v1/comment            → Access denied
POST /api/v1/comment?site=mysite → 201 返回完整评论 JSON

登录时 aud=mysite,发表时 URL 上必须补 ?site=mysite。前端组件会自动带上,手工调 API 时容易漏。

Q3:给自己刚发的评论投票返回 can't vote for comment?

这是 remark42 的防刷票规则:不允许给自己投票。实测完整回显:

{"code":13,"details":"can't vote for comment","error":"user anonymous_514e6574a9504050ad8895e9f7da2f9f4eb3f4b6 can not vote for his own comment cabcece9-360e-4f88-9feb-c10a8230cadf"}

用第二个账号投票则正常返回(实测 {"id":"cabcece9-360e-4f88-9feb-c10a8230cadf","score":1})。另注意投票接口与评论接口一样有更新类限流(默认 10 次/秒上限,超限返回 You have reached maximum request limit.),压测或脚本调用时留意间隔。

Q4:匿名登录接口返回的 JWT 多久过期,过期表现是什么?

默认 5 分钟(AUTH_TTL_JWT=5m)。过期后用原 JWT 调用受保护接口实测返回:

Unauthorized

重新调用 /auth/anonymous/login(或前端刷新会话)获取新 JWT 即可。AUTH_TTL_COOKIE 控制登录 cookie 的长效期(默认 200h),前端用户不受 5 分钟 JWT 的影响。

Q5:发表超过限制长度的评论会怎样?

服务端拒绝并返回明确错误。实测发送 4200 字符文本(MAX_COMMENT_SIZE 默认 2048,调到 4096 后):

{"code":4,"details":"invalid comment","error":"comment text exceeded max allowed size 4096 (4200)"}

长度按字符数计(一个汉字计 1)。需要更长评论时调大 MAX_COMMENT_SIZE。

Q6:服务起不来,systemctl status remark42 显示 failed 怎么排查?

按顺序查三处:① journalctl -u remark42 -n 50 看启动日志(最常见是 EnvironmentFile 路径写错或缺 SECRET/REMARK_URL,remark42 缺关键变量会直接退出并打印缺失项);② 确认端口未被占用(ss -ltnp | grep 8131);③ 确认数据目录属主是运行用户(ls -ld /opt/remark42/var,属主不对会导致 bolt 打不开数据库文件)。

十、卸载与清理

完全卸载 remark42 服务端与数据:

# 1. 停止并摘除自启
systemctl disable --now remark42
# 2. 删除服务单元
rm -f /etc/systemd/system/remark42.service && systemctl daemon-reload
# 3. 删除运行用户
userdel remark42
# 4. 删除程序与全部数据(评论/头像/图片/备份,不可恢复,确认后再执行)
rm -rf /opt/remark42 /opt/remark42-v1.17.1
# 5. 清理演示静态页(如有)
rm -rf /var/www/testpage

仅卸载程序保留评论数据:执行第 1–3 步后,只删除 /opt/remark42/bin/,保留 var/(数据)与 remark42.env(配置),后续重装同版本恢复目录结构即可无缝接管。

十一、参考链接