Anchr 配置:.env 全参数与 systemd 单元详解
Anchr 3.5.2 配置详解:.env 全参数逐项说明(监听/数据库/安全/SMTP/Telegram),config.js 加载机制、production 默认值陷阱与 systemd 单元逐行解读,全部经真机生效验证。
Anchr 配置:.env 全参数与 systemd 单元详解
实测基线:Anchr 3.5.2 + Ubuntu 22.04.5 LTS,配置加载逻辑以源码
config/config.js与config/express.js为准,全部结论经 2026-10-28 本机实测验证。前置安装见 《Anchr 安装:Ubuntu 22.04 源码构建与 systemd 服务化》。
一、配置加载机制:Anchr 如何读取配置
1.1 三层来源与优先级
Anchr 3.5.2 的全部配置来自环境变量,没有传统的 YAML/JSON 配置文件。运行时的加载链路是:
.env文件(键值对形式,位于应用根目录/opt/anchr/.env);env.sh脚本——内容只有一行export $(grep -v '^#' .env | xargs),作用是把.env中非注释行逐条 export 进当前 shell;config/config.js读取process.env.*并合成最终配置对象,NODE_ENV决定叠加 development/test/production 三套子配置。
用 systemd 托管后,EnvironmentFile=/opt/anchr/.env 由 systemd 原生完成第 2 步,无需再 source env.sh。生产环境改动配置的统一动作是:改 .env → systemctl restart anchr。
1.2 NODE_ENV 的隐性影响(易踩坑)
config.js 中 production 子配置有一个危险默认值——当 ANCHR_PUBLIC_URL 未设置时,公开地址回退到官方 SaaS 域名:
production: {
publicUrl: `${process.env.ANCHR_PUBLIC_URL || 'https://anchr.io'}/api`,
publicShortlinkUrl: `.../s`,
publicImageUrl: `.../i`,
clientUrl: `.../#/`,
}
实测后果:以 NODE_ENV=production 运行且忘配 ANCHR_PUBLIC_URL 时,创建短链返回的 href 会指向 https://anchr.io/s/xxxx——用户点开短链访问的是别人的 SaaS。自托管部署必须在 .env 中显式设置 ANCHR_PUBLIC_URL。
二、.env 核心参数逐项详解
2.1 服务监听与对外地址
| 变量 | 默认值 | 说明 |
|---|---|---|
PORT | 3000 | HTTP 监听端口,本系列实测取 8149 |
LISTEN_ADDR | localhost | 监听地址;对外服务必须改为 0.0.0.0,否则只能本机访问 |
ANCHR_PUBLIC_URL | http://localhost:3000 | 实例的公开基础地址,不带结尾斜杠;短链(/s/<id>)、图片(/i/<id>)、API(/api)前缀均由它拼出 |
实测验证:设 PORT=8149、LISTEN_ADDR=0.0.0.0、ANCHR_PUBLIC_URL=http://10.89.73.2:8149 后启动:
Express server listening on 0.0.0.0:8149
创建短链的返回体中 href 字段即为 ANCHR_PUBLIC_URL 拼接结果:
{"url":"https://www.quzuo.net/document/very-long-slug-example","_id":"u97g0","href":"http://10.89.73.2:8149/s/u97g0"}
2.2 数据库连接(五件套)
| 变量 | 默认值 | 说明 |
|---|---|---|
ANCHR_DB_USER | anchr | MongoDB 用户名 |
ANCHR_DB_PASSWORD | (空) | MongoDB 口令,应用启动必需 |
ANCHR_DB_HOST | localhost | 主机名 |
ANCHR_DB_PORT | 27017 | 端口 |
ANCHR_DB_NAME | anchr | 库名 |
连接串合成逻辑在 config.js 的 mongoConnectionString():五项拼成 mongodb://user:pass@host:port/dbname;若设置了 ANCHR_DB_URL 则整体覆盖五件套(连接副本集/带参数场景使用)。
配置正确时启动日志出现(实测):
Successfully established database connection.
2.3 安全与账号策略
| 变量 | 默认值 | 说明 |
|---|---|---|
ANCHR_SECRET | shhh(兜底) | JWT 签名密钥,必须设置为长随机串;泄露等于全体账号可被伪造登录态 |
ANCHR_ALLOW_SIGNUP | true | 是否开放注册;个人实例建库后建议改 false,用首个账号即可 |
ANCHR_VERIFY_USERS | false | 是否要求邮件激活账号;开启前必须先配好 SMTP |
ANCHR_BASIC_AUTH | true | 基础鉴权开关(config.js 中 !== 'false' 判定) |
生成强密钥并写入配置:
openssl rand -hex 16
实测回显:
3f5a91c2e8d74b6a90c1f2d3e4a5b6c7
master 分支最近一次提交(2026-09-22)正是 "fix: remove default value for secret (resolve #89)"——官方也意识到兜底密钥的风险,新版本将强制要求该变量。
2.4 文件与目录
| 变量 | 默认值 | 说明 |
|---|---|---|
ANCHR_UPLOAD_DIR | /var/data/anchr | 图片上传的落盘目录,必须预先创建,应用启动时不自动建目录 |
ANCHR_IMAGE_PROXY_URL_TPL | (空) | 可选图片代理模板(如 Caravaggio),留空禁用 |
ANCHR_LOG_PATH | /var/log/anchr/access.log | README 声明的访问日志路径 |
ANCHR_ERROR_LOG_PATH | /var/log/anchr/error.log | README 声明的错误日志路径 |
实测勘误(3.5.2):
ANCHR_LOG_PATH与ANCHR_ERROR_LOG_PATH在当前版本并未落地——config/log.js只是包装了console.log/console.error,两个日志文件始终为空,所有访问日志与错误堆栈实际输出到 stdout/stderr。用 systemd 托管后,统一用journalctl -u anchr查看即可,这两个变量可配可不配。
2.5 功能开关与第三方集成
| 变量 | 默认值 | 说明 |
|---|---|---|
ANCHR_EXPOSE_METRICS | false | 在 /api/metrics 暴露 Prometheus 指标 |
ANCHR_LINKCHECK_UPDATE_CRON | 10 0 * * * | 黑名单数据自动更新的 cron 表达式;3.5.2 源码中短链清理任务亦复用此变量(其独立默认值为 20 0 * * *)。链接安全检查本身无开关,恒启用 |
ANCHR_GOOGLE_API_KEY | (空) | Google Safe Browsing API Key;留空则降级为仅本地 blocklist,启动时打 WARN |
ANCHR_FB_CLIENT_ID / ANCHR_FB_SECRET | (空) | Facebook OAuth 登录凭据,留空禁用 |
ANCHR_GOOGLE_CLIENT_ID / ANCHR_GOOGLE_SECRET | (空) | Google OAuth 登录凭据,留空禁用 |
ANCHR_SMTP_HOST / ANCHR_SMTP_PORT / ANCHR_SMTP_TLS / ANCHR_SMTP_USER / ANCHR_SMTP_PASS | 空 / 587 / false | 邮件发送配置;ANCHR_SMTP_TLS=true 表示隐式 TLS(465 口),STARTTLS 不在此控制 |
ANCHR_MAIL_SENDER | Anchr.io noreply@anchr.io | 发件人显示地址 |
ANCHR_MAILWHALE_URL / ANCHR_MAILWHALE_CLIENT_ID / ANCHR_MAILWHALE_CLIENT_SECRET | https://mailwhale.dev | 用 MailWhale 服务替代 SMTP 发信 |
ANCHR_TELEGRAM_BOT_TOKEN / ANCHR_TELEGRAM_URL_SECRET | (空) | Telegram Bot 收藏入口;留空禁用 |
ANCHR_SHORTLINK_COLLECTION / ANCHR_IMAGE_COLLECTION(默认 My shortlinks / My images)控制首次使用时自动创建的两个默认收藏集名称。
2.6 Prometheus 指标实测
开启后重启:
sed -i 's/^ANCHR_EXPOSE_METRICS=false/ANCHR_EXPOSE_METRICS=true/' /opt/anchr/.env
systemctl restart anchr
curl -s http://127.0.0.1:8149/api/metrics | head -3
实测回显:
# HELP anchr_process_cpu_user_seconds_total Total user CPU time spent in seconds.
# TYPE anchr_process_cpu_user_seconds_total counter
anchr_process_cpu_user_seconds_total 0.349286
实测指标序列共 91 条,覆盖 anchr_*(进程级)、express_*(HTTP 请求计数/时延直方图)、nodejs_*(事件循环/GC)。Prometheus 抓取配置示例:
scrape_configs:
- job_name: anchr
metrics_path: /api/metrics
static_configs:
- targets: ['<服务器IP>:8149']
三、systemd 单元逐行解读
生产单元文件(B1 篇已创建,此处解读关键行):
[Unit]
Description=Anchr self-hosted bookmarks toolbox
After=network-online.target mongod.service # 确保 MongoDB 先起
Wants=network-online.target
[Service]
Type=simple
User=anchr # 专用无登录权限系统用户,不跑 root
Group=anchr
WorkingDirectory=/opt/anchr # app.js 依赖相对路径,必须固定
Environment=NODE_ENV=production
EnvironmentFile=/opt/anchr/.env # systemd 原生加载 .env,替代 env.sh
ExecStart=/usr/bin/node app.js
Restart=on-failure # 异常退出 5 秒后自动拉起
RestartSec=5
[Install]
WantedBy=multi-user.target
改过单元文件后必须 systemctl daemon-reload 再 restart。验证进程属主:
ps -o user,cmd -C node
实测回显:
USER CMD
anchr /usr/bin/node app.js
四、配置排障实录
4.1 直连 API 查书签报 500($slice -nan)
现象:GET /api/collection/<id>/links 不带查询参数时返回:
{"error":"PlanExecutor error during aggregation :: caused by :: Second argument to $slice can't be represented as a 32-bit integer: -nan","status":500}
根因:3.5.2 的分页参数处理为 Math.max(req.query.page, 1),参数缺失时 Math.max(undefined, 1) 得 NaN,直接进了 MongoDB $slice。网页端不受影响(前端始终带分页参数),只有脚本直连 API 会踩到。解法:显式携带分页参数:
curl -s "http://127.0.0.1:8149/api/collection/<id>/links?page=1&pageSize=20&q=关键词" \
-H "Authorization: Bearer <TOKEN>"
实测回显:
[{"url":"https://ubuntu.com/server/docs","description":"Ubuntu Server 官方操作指南","date":"2026-10-28T03:25:19.465Z","_id":"6ae16b1f0b0771ec4192d823"}]
4.2 图片上传 500:表单字段名必须叫 uploadFile
现象:curl -F "file=@x.png" 上传返回 HTTP 500 Internal Server Error。根因:服务端 config/middlewares/filetype.js 硬编码了 FILE_UPLOAD_FIELD = 'uploadFile',字段名不符时其类型校验分支在 undefined 上取 .path 直接抛异常。正确姿势:
curl -s -X POST http://127.0.0.1:8149/api/image \
-H "Authorization: Bearer <TOKEN>" \
-F "uploadFile=@/path/to/real.png"
实测回显:
{"_id":"p39cy.png","source":"upload","encrypted":false,"type":"image/png","href":"http://10.89.73.2:8149/i/p39cy.png"}
随后 GET /i/p39cy.png 返回 HTTP 200,文件落在 $ANCHR_UPLOAD_DIR/p39cy.png。
4.3 短链 href 指向 anchr.io
现象:短链创建成功,但 href 是 https://anchr.io/s/xxxx。根因见 2.2 节——production 模式下 ANCHR_PUBLIC_URL 缺省回退官方域名。解法:.env 显式设置后 systemctl restart anchr。
4.4 数据库连不上(ECONNREFUSED 27017)
现象:启动卡住或日志出现 MongoNetworkError: connect ECONNREFUSED 127.0.0.1:27017。排查顺序:
systemctl is-active mongod # 先看数据库本身
journalctl -u mongod -n 20 # 再看数据库日志
ss -ltn | grep 27017 # 确认监听
实测中该错误出现过一次,原因是 mongodb-org 半安装状态导致 mongod 未起来,dpkg --configure -a 补完后恢复正常(详见 B1 篇排障实录 ②)。
4.5 短链创建全部报 400 "not safe"
现象:POST /api/shortlink 无论什么 URL 一律返回 {"error":"The link you try to reference is not safe!","status":400}。根因是 3.5.2 恒启用的链接安全检查对当前上游黑名单数据全量误判(uBlock 列表 ipaddress= 新语法被老解析库忽略修饰符后泛匹配 .com、PhishingArmy 快照混入 co 垃圾条目被子串匹配命中)。完整定位链、数据清洗处置与复发规避见运维篇第六节。
五、配置变更的标准动作
vim /opt/anchr/.env
systemctl restart anchr
systemctl is-active anchr
curl -s -o /dev/null -w "HTTP %{http_code}\n" http://127.0.0.1:8149/
预期:active + HTTP 200。任何 .env 修改都以"改文件 → 重启 → 三连验证"收尾,不做热加载幻想。
后续的日常启停、备份与升级操作,见 《Anchr 命令运维:服务管理、备份恢复与版本升级》;选型背景与功能全景见 《Anchr 认知选型:三合一轻量书签工具箱的定位、活跃度与替代方案》。