Turtl 0.1.3 服务器配置参数详解
逐项讲清 Turtl server 0.1.3 的 config.yaml 参数、环境变量覆盖、加密盐与登录锁定、同步限额、插件与附件存储,并给出生产配置模板。
Turtl 0.1.3 服务器配置参数详解
目标:把 Turtl server 0.1.3 的全部配置面——
config.yaml各段参数、环境变量覆盖机制、加密盐与登录锁定、同步限额、插件与附件存储——逐项讲清默认值与影响,并给出生产配置模板。参数默认值取自官方仓库config/config.yaml.default与发布件内置config/config.yaml.docker(turtl/server 源码,查询日期 2026-11-02),生效行为在 Ubuntu 26.04 部署环境实测验证(部署见本专栏 Docker Compose 部署篇)。
一、环境信息
- 被测软件:Turtl server 0.1.3(
turtl/server源码构建,运行时基座 node:11-alpine),配置文件位于源码树config/config.yaml(部署后为/app/config/config.yaml,构建时由config.yaml.docker拷入) - 配置加载入口:
helpers/config.js(yaml_env 库 + 环境变量覆盖) - 部署形态:Docker Compose(
turtl-server:0.1.3+postgres:11-alpine,宿主端口 8144),环境变量覆盖通道实测可用 - 参考默认值文件:仓库
config/config.yaml.default(裸机默认)与config/config.yaml.docker(发布件基线),两者差异见第四章总览表
二、前置条件
- 服务已按 《Turtl 0.1.3 Docker Compose 部署指南》 跑通且健康:
curl -s http://127.0.0.1:8144/ ; echo; curl -s http://127.0.0.1:8144/health/db
{"greeting":"Hi."}
{"healthy":true}
- 需要核对参数语义时取源码(本文所有行为描述均以源码为准):
git clone https://github.com/turtl/server.git
三、参数查看与修改方式
Turtl 只有一个配置面:config/config.yaml(YAML),但有三条修改通道,优先级与生效条件不同(加载链见 helpers/config.js,按执行顺序):
| 通道 | 方式 | 生效条件 | 适用 |
|---|---|---|---|
| 直接改 YAML | 编辑 config/config.yaml(裸机改文件;Docker 部署需重新构建发布件或挂载同名文件并重启) | 重启进程 | 整份基线配置 |
| 环境变量覆盖 | TURTL_<YAML 路径大写>,如 app.api_url → TURTL_APP_API_URL、db.host → TURTL_DB_HOST | docker compose up -d 重建服务 | 单项覆盖,本文部署实测通道 |
| 专用环境变量 | TURTL_CONFIG_FILE(换配置文件名)、DATABASE_URL(postgres:// 串整体覆盖 db.*)、PORT(覆盖 server.port)、TURTL_CONFIG_OVERRIDE(JSON 串按顶层键整段替换) | 重启进程 | 特殊场景 |
加载链源码事实(helpers/config.js):
var config_file = 'config.yaml';
if(process.env['TURTL_CONFIG_FILE']) {
config_file = process.env['TURTL_CONFIG_FILE'];
}
var config = yaml_env.load('TURTL', __dirname+'/../config/'+config_file);
// 之后依次: DATABASE_URL 拆填 db.* → PORT 覆盖 server.port → TURTL_CONFIG_OVERRIDE 逐顶层键替换
⚠️ 注意:环境变量前缀是
TURTL_(yaml_env.load('TURTL', ...)的第一个参数)。官方 README 正文把前缀误写为TURTLE_,但其示例TURTL_APP_API_URL才是正确形态——照 README 正文写变量名会静默不生效。
配置没有「查询当前值」的 API 端点,验证手段按参数类型选择:启动日志(监听端口、控制器加载清单)、行为回显(健康检查、认证、同步限额)、访问日志(loglevel 影响 morgan 输出级别)。
四、参数总览
config.yaml.default 与发布件基线 config.yaml.docker 的全部键对照(默认值列自 default 文件原文):
| 段.参数 | default 值 | docker 基线值 | 说明 |
|---|---|---|---|
server.host | 空(全接口) | 空 | 监听地址,注释:默认监听所有 IP,可用此参数指定 |
server.port | 8181 | 8181 | 监听端口;PORT 环境变量可覆盖 |
db.connstr | 'postgres://slappy:floppy@127.0.0.1:5432/turtl' | 未设 | 整串连接;DATABASE_URL 环境变量优先级更高 |
db.host/port/database/user/password | 未设(走 connstr) | 'db'/5432/'turtl'/'turtl'/'' | 五元组形式,TURTL_DB_* 覆盖 |
db.pool | 24 | 24 | 连接池大小 |
loglevel | 'debug' | 'info' | winston 日志级别 |
app.enable_bookmarker_proxy | false | false | 须写魔法字符串才加载 bookmarking 控制器 |
app.api_url | 'http://api.yourdomain.com:8181' | 'http://127.0.0.1:8181' | 服务器对外 API 地址(无尾斜杠) |
app.www_url | 'https://yourdomain.com' | 'https://yourdomain.com' | 官网地址(确认/删除跳转目标) |
app.login.max_attemps | 5 | 5 | 失败登录锁定阈值,-1 关闭(键名拼写就是 attemps) |
app.login.lock_duration | 60 | 60 | 锁定时长(秒) |
app.emails.admin/info/invites | admin@turtlapp.com 等 | 同 default | 系统邮件收件人/发件人 |
app.secure_hash_salt | 官方示例长句 | 同 default(构建期被环境变量顶替) | 全局加密盐,必填必填再必填(5.4) |
app.allow_unconfirmed_invites | false | true | 未确认账号可否发邀请 |
sync.max_bulk_sync_records | 32 | 32 | 单次批量同步上限,超出静默截断 |
plugins.plugin_location | '/var/www/turtl/server/plugins' | '/plugins' | 插件根目录 |
plugins.email.enabled/endpoint/defaults | false/'smtps://user:password@smtp.gmail.com/?pool=true'/{} | false/空/{} | 邮件插件(确认/邀请信) |
plugins.analytics.enabled | 未设 | false | 统计插件 |
plugins.premium.enabled | 未设 | false | 付费功能插件 |
uploads.local | '/var/www/turtl/server/public/uploads' | '/uploads' | 本地附件目录;设 false 则走 S3 |
uploads.local_proxy | true | true | 本地文件经服务端代理下载(免 CORS) |
uploads.url | 'http://api.turtl.dev/uploads' | 'http://127.0.0.1:8181/uploads' | 不代理时附件的公开 URL 前缀 |
s3.token/secret/bucket/endpoint/pathstyle | 占位值/'https://s3.amazonaws.com'/false | 同 default | S3 附件存储备选 |
两处 docker 基线与 default 的关键差异:db.host: 'db'(compose 服务名)与 allow_unconfirmed_invites: true(方便测试);生产务必按第六章改回。
五、核心参数分类详解
5.1 server 监听与数据库连接
server.host留空时启动日志为info: Listening for turtls on port 8181...(实测原文);设置具体 IP 后日志变为Listening for turtls on IP <地址>, port 8181...(源码server.js第 66–72 行分支)。db.connstr与五元组二选一;DATABASE_URL环境变量存在且以postgres:开头时,helpers/config.js解析 URL 并拆填db.host/port/database/user/password,优先级高于 YAML 与环境变量五元组。db.pool默认 24:连接池上限,单机小团队保持默认即可。- 生效验证(本文部署以
TURTL_DB_*五元组覆盖发布件基线db.host: 'db'):
curl -s http://127.0.0.1:8144/health/db
{"healthy":true}
5.2 loglevel 与访问日志
loglevel 取 winston 级别(debug/info),default 文件是 debug、发布件基线是 info。除应用日志外,server.js 挂 morgan 中间件输出逐请求访问日志,格式串(源码原文):
morgan(':remote-addr ":method :url" :status :res[content-length]', ...)
实测访问日志行(部署环境回显):
info: ::ffff:10.89.61.1 "GET /" 200 18
info: ::ffff:10.89.61.1 "GET /health/db" 200 16
排障同步协议(如 5.8 的静默截断)时临时调 TURTL_LOGLEVEL: debug 重建服务。
5.3 app.api_url 与 app.www_url
app.api_url:服务器对外 API 地址(注释明示 no trailing slash)。确认/删除邮件中的链接以此为目标,反向代理 + TLS 部署时必须填公网 https 地址,否则用户点邮件链接会打到内网地址。本文部署实测值http://127.0.0.1:8144(经TURTL_APP_API_URL覆盖)。app.www_url:官网/前端地址。controllers/users.js的确认与删除流程用tres.redirect(res, config.app.www_url+'/users/confirm/success', ...)做 307 跳转——即确认链接点完后浏览器被导向<www_url>/users/confirm/success,填错会跳到不存在的前端。
5.4 app.secure_hash_salt(全局加密盐,最高风险参数)
源码用途有两处(models/user.js):
- 登录凭据落库:
pbkdf2(auth, secure_hash_salt, 2 次迭代, base64)——auth 是客户端生成的不透明 blob,低迭代次数是源码注释明示的有意设计; - 服务端私有数据加密:
pbkdf2(privatedata, secure_hash_salt, 100000 次迭代, sha256)。
硬约束与风险:
- 入口脚本强制校验:未设
TURTL_APP_SECURE_HASH_SALT时打印TURTL_APP_SECURE_HASH_SALT is unset.并exit 1,进程根本无法启动(本文部署实测通道)。 - 上线后更换盐值 = 所有既有账号 auth 校验立即失效(库里存的是旧盐的 pbkdf2 结果),且私有数据加密随之失效。生成一次(如
openssl rand -hex 32)、备份、永不轻改。 - default 文件里那句「Plaque is a figment...」长文本是官方占位示例,生产必须替换。
5.5 app.login 登录锁定
check_auth(models/user.js 第 89–110 行)流程:查 users 表 → active 检查 → 失败锁定检查 → secure_compare(user.auth, pbkdf2(auth, salt, 2))。
| 参数 | 默认 | 行为 |
|---|---|---|
max_attemps | 5 | 失败次数达到阈值即锁;设 -1 关闭锁定(注释原文)。键名拼写为 attemps(上游如此),写成 attempts 不生效 |
lock_duration | 60 | 锁定时长(秒),窗口内再错继续拒绝 user locked |
失败计数落 users.login_failed_count/login_failed_last 字段(注册回显里即可见这两个字段初值 0/null)。
5.6 app.allow_unconfirmed_invites 与 app.emails
allow_unconfirmed_invites:控制「账号未确认时能否发送共享邀请」。defaultfalse(生产稳妥),docker 基线true(方便测试)——生产部署建议改回 false。app.emails.admin/info/invites:系统邮件的收件人与发件人署名,配合 email 插件使用。
5.7 app.enable_bookmarker_proxy
书签导入代理开关。server.js 加载控制器列表时,bookmarking 一项要求配置值精确等于字符串 I UNDERSTAND THIS VIOLATES THE PRIVACY OF MY USERS 才加载(default 文件注释原文:ALWAYS false in production. Always.)。实测启动日志的控制器清单没有 bookmarking:
info: Loading controller: users
info: Loading controller: sync
info: Loading controller: spaces
info: Loading controller: files
info: Loading controller: feedback
info: Loading controller: errlog
info: Loading controller: cla
info: Loading controller: health
该功能会让服务器代客户端抓取 URL(历史书签导入用),与零知识定位冲突,官方用「手写隐私声明」的方式阻止误开。
5.8 sync.max_bulk_sync_records
单次 POST /sync 允许的最大记录数,默认 32。源码行为(models/sync.js 第 405–410 行):
var max_sync_records = (config.sync || {}).max_bulk_sync_records;
if(max_sync_records) {
sync_records = sync_records.slice(0, max_sync_records);
}
⚠️ 注意:超出部分是 slice 静默截断——不报错、不进 failures,客户端若一次推 40 条会只落 32 条且响应看不出异常。大批量推送必须客户端自行分片(每片 ≤32),这是自写客户端最容易踩的限额。
5.9 plugins 插件段
plugins.plugin_location 指向目录(部署目录内 /plugins,构建时 install-plugins.sh 装入),目录下每个含 main.js 的子目录即一个插件(server.js 第 51–64 行遍历加载)。配置键与目录同名,每插件至少含 enabled 布尔:
email:确认信/邀请信投递(nodemailer endpoint,smtps://user:password@host/?pool=true形式)。enabled false 时确认邮件静默跳过——注册成功但账号停在confirmed:false(实测回显见部署篇 4.5),分享邀请受限。analytics/premium:统计与付费钩子,自托管默认关。
5.10 uploads 与 s3(附件存储)
uploads.local非 false 时附件写本地目录(部署目录内/uploads——本地磁盘路径,重装即丢,生产要么挂出来要么改 false 走 S3)。uploads.local_proxy: true时下载走服务端代理路由GET /notes/:note_id/local-attachment-proxy(免配 web 服务器 CORS,注释原话:高并发下略影响性能)。local_proxy: false时uploads.url必须是附件对客户端可达的公开 URL 前缀。s3.{token,secret,bucket,endpoint,pathstyle}:本地存储设 false 后的备选;pathstyle: true用于 MinIO 等非 AWS 兼容端点。
六、生产配置模板
适用场景:16GB 内存 / 50GB SSD 单机、10 人以内团队笔记、经反向代理以 https 对外:
server:
host:
port: 8181
db:
host: 'postgres-db'
port: 5432
database: 'turtl'
user: 'turtl'
password: 'q1w2e3r4'
pool: 24
loglevel: 'info'
app:
enable_bookmarker_proxy: false
api_url: 'https://notes.example.com' # 反向代理后的公网 API 地址,无尾斜杠
www_url: 'https://www.example.com' # 你的前端(www 仓库自部署)地址
login:
max_attemps: 5
lock_duration: 60
emails:
admin: 'admin@example.com'
info: 'Turtl <info@example.com>'
invites: 'invites@example.com'
secure_hash_salt: '<openssl rand -hex 32 生成,备份后永不更改>'
allow_unconfirmed_invites: false
sync:
max_bulk_sync_records: 32
plugins:
plugin_location: '/plugins'
email:
enabled: true
endpoint: 'smtps://user:password@smtp.example.com/?pool=true'
defaults: {}
uploads:
local: '/uploads' # 务必在 compose 里挂载 ./uploads:/uploads
local_proxy: true
url: 'https://notes.example.com/uploads'
s3:
token: ''
secret: ''
bucket: ''
endpoint: 'https://s3.amazonaws.com'
pathstyle: false
要点:盐值一次生成永久使用;api_url/www_url 填公网域名否则邮件链接与 307 跳转会坏;email 插件配好 SMTP 才能让成员完成确认、开放共享;/uploads 必须持久化挂载或改走 S3。Docker 形态下以上任意项也可不碰文件,直接用 TURTL_<路径大写> 环境变量覆盖(第三章)。
七、常见问题
Q1:环境变量设了 TURTLE_APP_API_URL 没生效?
前缀写错。README 正文的 TURTLE_ 是笔误,实际前缀由 yaml_env.load('TURTL', ...) 决定,是 TURTL_(如 TURTL_APP_API_URL)。改完 docker compose up -d 重建服务生效。
Q2:改了 compose 里的环境变量,服务行为没变?
配置只在进程启动时读取一次,docker compose restart 不会重读 compose 文件里的 environment——必须 docker compose up -d(检测到配置差异时重建服务)。
Q3:换了 secure_hash_salt 后所有客户端都报 bad login: (bad auth)?
预期行为(5.4):库里 auth 是旧盐的 pbkdf2 结果,新盐校验必然失败且不可逆。恢复旧盐是唯一出路,这就是「盐值备份、永不轻改」的原因。
Q4:一次推 40 条 sync 记录,只落了 32 条且无报错?
max_bulk_sync_records 的 slice 静默截断(5.8)。客户端按 ≤32 分片推送,或调大该参数(不建议,防滥用初衷见 default 文件注释)。
Q5:注册后一直 confirmed:false,邀请发不出去?
email 插件 enabled: false 时确认邮件被静默跳过(5.9)。配好 SMTP 后调 POST /users/confirmation/resend 重发确认,或临时开 allow_unconfirmed_invites(仅测试环境)。
Q6:想启用书签导入代理?
把 app.enable_bookmarker_proxy 精确设为 I UNDERSTAND THIS VIOLATES THE PRIVACY OF MY USERS(5.7),启动日志出现 Loading controller: bookmarking 即生效;生产环境请三思——它让服务器代抓 URL,违背零知识定位。
八、参考链接
- 默认配置原文:
turtl/server仓库config/config.yaml.default、config/config.yaml.docker(查询日期 2026-11-02) - 配置加载源码:
helpers/config.js、server.js、models/user.js、models/sync.js(turtl/server 0.1.3) - 部署与环境变量实测:见 《Turtl 0.1.3 Docker Compose 部署指南》
- 配置生效的接口侧验证:见 《Turtl 0.1.3 REST API 使用详解》
- 同类自托管组件配置篇写法可对照 《TeamMapper 0.4.0 配置参数详解》