Actual 配置参数详解
Actual sync-server(v26.10.0)配置参数详解:环境变量全集与 config.json 键位、三层优先级实测(env>config>默认)、数据目录派生树实测、password/header/openid 登录体系与 OpenID 全套变量、上传限制三变量,并澄清 ACTUAL_UPLOAD_DIR 不存在等 6 条 FAQ。
Actual 配置参数详解
目标:系统讲清 Actual sync-server(同步服务器)的配置参数体系——环境变量全集、config.json 键位、三层优先级、参数如何改变数据目录派生结构,以及 OpenID 登录等进阶配置的落地方式。全部参数行为均在 v26.10.0 上实测验证(实测日期 2026-10-28),每个关键结论附实测回显;部署流程本身见 《Actual Docker Compose 部署实战》。
一、环境信息
- 组件版本:Actual sync-server v26.10.0(官方最新稳定版,
latest标签) - 实测环境:Ubuntu 24.04(x86_64),Node.js 24(服务端运行时)
- 配置加载源码:
packages/sync-server/src/load-config.js(官方文档明确提示:环境变量与 config.json 键并非一一对应,存疑时以该 schema 为准) - 实测方法:以 CLI 方式启动 sync-server 进程,注入不同环境变量组合,观察启动日志监听行与数据目录派生结构;探活用
/health端点(返回{"status":"UP"}) - 示例密码:
q1w2e3r4(演示口令)
💡 说明:sync-server 是一个 Node.js 单进程服务,没有独立配置数据库——所有配置都是进程启动时一次性读取(环境变量 / config.json / CLI 参数),改配置即重启进程。理解"启动时读定,之后不再变更"这一点,可以避免大量"改了配置为什么不生效"的困惑。
二、配置参数体系总览
2.1 三个配置入口与优先级
官方文档口径,sync-server 启动时按以下顺序查找并合并配置,优先级从高到低:
| 层级 | 入口 | 说明 |
|---|---|---|
| 1(最高) | 环境变量(ACTUAL_*) | 容器编排、systemd 单元、CLI 内联注入的首选方式 |
| 2 | config.json | 按需 ACTUAL_CONFIG_PATH 指定路径,否则找 /data/config.json(存在时)或安装目录 |
| 3(默认) | 内置默认值 | 端口 5006、监听 ::、数据目录 /data(存在时)等 |
实测验证优先级:环境变量 ACTUAL_PORT=5007 与 config.json 的 "port": 5006 同时存在时,监听端口取环境变量值:
Listening on :::5007...
去掉环境变量、仅保留 config.json 后重启:
Listening on :::5006...
两组回显证明:环境变量覆盖 config.json,config.json 覆盖默认值。
2.2 键名映射规则与 _FILE 密钥后缀
- config.json 键为 camelCase(
dataDir、serverFiles、upload.fileSizeLimitMB),对应环境变量为ACTUAL_+ 大写下划线(ACTUAL_DATA_DIR、ACTUAL_SERVER_FILES、ACTUAL_UPLOAD_FILE_SIZE_LIMIT_MB); - 并非所有键都有环境变量形态(如
https是嵌套对象,通常只能写 config.json),也并非所有环境变量都有一一对应的键——官方把权威 schema 放在load-config.js; - Secrets 文件化:
ACTUAL_OPENID_CLIENT_SECRET_FILE、ACTUAL_GITHUB_TOKEN_FILE等_FILE后缀变量可指向含密钥的文件(适配 Docker secrets / Kubernetes secret 卷),且_FILE后缀优先于普通变量。CLI 自身变量同样支持该后缀。
2.3 生效方式
所有配置在进程启动时读定。修改配置的标准流程:
# CLI 方式:改环境变量或 config.json 后重启进程
# systemd 部署:systemctl restart actual
# 容器部署:改 compose 的 environment/volumes 后 docker compose up -d
验证生效最直接的信号是启动日志的监听行(Listening on <host>:<port>...)与数据目录里新增的子目录(见第四章)。
三、网络监听参数
| 参数 | 默认值 | 说明 |
|---|---|---|
ACTUAL_PORT(config: port) | 5006 | HTTP(S) 监听端口 |
ACTUAL_HOSTNAME(config: hostname) | :: | 监听地址(多数系统上同时覆盖 IPv4/IPv6) |
ACTUAL_HTTPS_KEY(config: https.key) | 无 | TLS 私钥文件路径 |
ACTUAL_HTTPS_CERT(config: https.cert) | 无 | TLS 证书文件路径 |
ACTUAL_TRUSTED_PROXIES | 10.0.0.0/8, 172.16.0.0/12, 192.168.0.0/16, fc00::/7, ::1/128 | 信任的反向代理网段(从客户端 IP 链中去掉代理 IP,影响限流判定) |
ACTUAL_TRUSTED_AUTH_PROXIES | 同上 | 允许以 HTTP 头方式认证的客户端(header 登录方式配套) |
实测:改端口。注入 ACTUAL_PORT=5007 启动:
启动日志实测回显:
Checking if there are any migrations to run for direction "up"...
Migrations: DONE
Running in production mode - Serving static React app
Listening on 0.0.0.0:5007...
/health 探活实测回显:
{"status":"UP"}
💡 说明:
ACTUAL_HOSTNAME不设置时监听::(回显里表现为Listening on :::5006...);显式设为0.0.0.0时回显为Listening on 0.0.0.0:5007...。两者在常见 Linux 上对外可达性一致,生产建议显式指定以便审计。
HTTPS 说明:同时提供 key 与 cert 后服务直接以 HTTPS 监听(证书需自备,自签场景官方文档有专章);多数生产形态是反向代理终结 TLS、后端保持 HTTP,此时只需关注 ACTUAL_TRUSTED_PROXIES 把代理网段配对。
四、数据目录参数(含目录树实测)
4.1 参数定义
| 参数 | 默认值 | 说明 |
|---|---|---|
ACTUAL_DATA_DIR(config: dataDir) | /data(该目录存在时),否则当前目录 | 数据总目录 |
ACTUAL_CONFIG_PATH | 无 | config.json 路径(默认在数据目录或安装目录找;不能写进 config.json 本身——鸡生蛋问题) |
ACTUAL_SERVER_FILES(config: serverFiles) | <dataDir>/server-files | 服务器账号库目录(account.sqlite:密码哈希/预算清单/会话) |
ACTUAL_USER_FILES(config: userFiles) | <dataDir>/user-files | 预算文件目录(全部预算 blob 与同步组库) |
ACTUAL_WEB_ROOT(config: webRoot) | 包内 @actual-app/web | 前端静态资源目录(自定义前端才需要) |
4.2 默认派生结构实测
全新数据目录首次启动后实查:
/data
├── .migrate
├── server-files
│ └── account.sqlite
└── user-files
创建预算文件后再实查(du -b 实测大小):
/data
├── .migrate
├── server-files
│ └── account.sqlite (69632 B)
└── user-files
├── file-29b70409-25fa-47ba-b410-6c553ed0a32a.blob (12017 B)
└── group-caa22e62-9d2e-48d9-a781-b2b1455e38a1.sqlite (24576 B)
命名规律:预算文件本体是 file-<cloudFileId>.blob,同步组库是 group-<groupId>.sqlite。.migrate 与 account.sqlite 落在 server-files,而 .migrate 跟随数据总目录。
4.3 自定义路径派生实测
三个变量拆开指定(ACTUAL_DATA_DIR=/srv/actual-store、ACTUAL_SERVER_FILES=/srv/actual-store/accounts-db、ACTUAL_USER_FILES=/srv/actual-store/budget-blobs)后首次启动,实查目录树:
/srv/actual-store
├── accounts-db
│ └── account.sqlite
├── budget-blobs
└── .migrate
实测结论:目录自动创建(无需手工 mkdir 预建),account.sqlite 精确落在 ACTUAL_SERVER_FILES 指定路径,budget-blobs 目录按 ACTUAL_USER_FILES 创建。该形态适用于"账号库与预算文件分区存放"或"预算文件单独挂大容量盘"的运维需求。
⚠️ 注意:拆分存放后,备份必须覆盖两个目录(账号库与预算文件),漏掉任何一个都无法完整恢复(账号库没有预算文件、预算文件没有账号密码)。
五、认证与登录参数
5.1 参数定义
| 参数 | 默认值 | 说明 |
|---|---|---|
ACTUAL_LOGIN_METHOD(config: loginMethod) | password | 默认登录方式:password / header / openid |
ACTUAL_ALLOWED_LOGIN_METHODS | password,header,openid | 允许的登录方式白名单(逗号分隔) |
ACTUAL_SIMPLEFIN_ALLOWED_NETWORKS | 无(默认禁止私网) | 允许 SimpleFIN 访问的内网桥 IP/CIDR(自建 SimpleFIN 桥才需要) |
5.2 password 模式(默认)实测闭环
服务器首次上线需先"引导"设置密码,全套实测回显如下。
查询引导状态:
curl -sS http://127.0.0.1:8129/account/needs-bootstrap
{"status":"ok","data":{"bootstrapped":false,"loginMethod":"password","availableLoginMethods":[],"multiuser":false}}
引导设置密码(bootstrap):
curl -sS -X POST http://127.0.0.1:8129/account/bootstrap \
-H 'Content-Type: application/json' -d '{"password":"q1w2e3r4"}'
{"status":"ok","data":{"token":"d43b7717-e7a7-409e-a74b-9df059ee78ae"}}
登录换会话 token:
curl -sS -X POST http://127.0.0.1:8129/account/login \
-H 'Content-Type: application/json' -d '{"password":"q1w2e3r4"}'
{"status":"ok","data":{"token":"d43b7717-e7a7-409e-a74b-9df059ee78ae"}}
引导后状态复核:
{"status":"ok","data":{"bootstrapped":true,"loginMethod":"password","availableLoginMethods":[{"method":"password","active":1,"displayName":"Password"}],"multiuser":false}}
💡 说明:
availableLoginMethods是运行时可用的登录方式列表,由ACTUAL_ALLOWED_LOGIN_METHODS与实际配置共同决定——这是排查"登录方式怎么不生效"的第一观察点。密码哈希存于server-files/account.sqlite,所以改密码/重置密码的本质是动这个库(CLI 提供--reset-password,见第八章)。
5.3 header 模式要点
header 方式让网关在请求头 x-actual-password 注入密码实现自动登录,仅建议"已有统一认证入口"的场景使用;且必须配合 ACTUAL_TRUSTED_AUTH_PROXIES 限定哪些来源可以带认证头,否则任何能直连服务的客户端都可以伪造该头登入(官方文档明确提示安全风险)。收敛白名单的标准做法:
ACTUAL_TRUSTED_AUTH_PROXIES=10.89.0.0/16
5.4 loginMethod 三种方式对比
| 方式 | 机制 | 适用 | 风险点 |
|---|---|---|---|
password(默认) | 服务端校验密码,签发 token | 个人自托管标准形态 | 弱密码可被爆破,建议强密码 + 反代限流 |
header | 网关注入 x-actual-password 自动登录 | 已有 SSO 网关注入凭据 | 信任来源配置错误即任意登入 |
openid | 对接 OIDC/OAuth2 身份提供方 | 多用户、要 MFA 的家庭/团队 | 见第六章配置项较多 |
六、OpenID 登录参数详解
openid 是官方多用户与 MFA 的推荐方案(预览特性),支持两种配置通道:环境变量或 config.json(亦可在 Web 界面预算内 Settings 中图形化配置)。核心环境变量如下:
发现(discovery)模式——身份提供方支持 .well-known/openid-configuration 时只需四项:
| 变量 | 示例值 |
|---|---|
ACTUAL_OPENID_DISCOVERY_URL | https://<provider>/.well-known/openid-configuration |
ACTUAL_OPENID_CLIENT_ID | my-actual-app |
ACTUAL_OPENID_CLIENT_SECRET | super-secret-value |
ACTUAL_OPENID_SERVER_HOSTNAME | https://actual.example.com(服务公网地址,回调用) |
手动端点模式(提供方不支持 discovery 时):以 ACTUAL_OPENID_AUTHORIZATION_ENDPOINT、ACTUAL_OPENID_TOKEN_ENDPOINT、ACTUAL_OPENID_USERINFO_ENDPOINT 三个端点变量替代 discovery URL。
行为与安全控制项:
| 变量 | 可选值 | 说明 |
|---|---|---|
ACTUAL_OPENID_AUTH_METHOD | openid(默认)/ oauth2 | GitHub 这类不完全支持 OIDC discovery 的提供方用 oauth2 |
ACTUAL_OPENID_ENFORCE | true/false(默认) | true 时 OpenID 成为唯一登录方式 |
ACTUAL_TOKEN_EXPIRATION | never(当前默认)/ openid-provider / 秒数(如 3600) | 会话 token 过期策略 |
ACTUAL_USER_CREATION_MODE | manual(默认)/ login | manual:用户须先在 Actual 侧建好同名账号;login:首次认证自动建户 |
ACTUAL_OPENID_CLIENT_SECRET_FILE | 文件路径 | 密钥文件化(优先于普通变量) |
落地要点(官方文档口径 + 配置实践):
- 身份提供方需登记回调地址:
https://<actual服务器>/openid/callback; - 第一个用 OpenID 登录的用户自动成为服务器 owner(且 UI 不可更改)——全新实例上线时谁先登录谁是管理员,要有预期;
- 官方列出已验证提供方:Auth0、Authentik、GitHub、Google Accounts、Keycloak、Microsoft Entra、Passwordless.id、Pocket ID、Rauthy(Rauthy 需将 token/ID token 算法改为 RS256);
- config.json 形态的键(
openId对象:discoveryURL/client_id/client_secret/server_hostname/authMethod)与环境变量二选一即可,键名风格与主配置一致(camelCase/snake_case 混用以官方示例为准)。
七、上传限制参数
| 参数 | 默认值 | 说明 |
|---|---|---|
ACTUAL_UPLOAD_FILE_SIZE_LIMIT_MB(config: upload.fileSizeLimitMB) | 20 | 普通上传文件上限(导入 OFX/CSV 等走这里) |
ACTUAL_UPLOAD_FILE_SYNC_SIZE_LIMIT_MB(config: upload.fileSyncSizeLimitMB) | 20 | 同步通道文件上限 |
ACTUAL_UPLOAD_SYNC_ENCRYPTED_FILE_SYNC_SIZE_LIMIT_MB(config: upload.syncEncryptedFileSizeLimitMB) | 50 | 加密同步文件上限(加密膨胀所以默认更宽) |
实测澄清:不存在 ACTUAL_UPLOAD_DIR 这个配置项。实测向进程注入 ACTUAL_UPLOAD_DIR=/data/uploads-dir 后:
启动日志(正常启动,无任何关于 upload 的告警——未知变量被静默忽略):
Migrations: DONE
Running in production mode - Serving static React app
Listening on :::5007...
数据目录实查(uploads-dir 并未被创建,上传落点仍是默认的 user-files):
/data
├── server-files
└── user-files
结论:上传目录没有独立开关——导入/上传的文件统一落在 ACTUAL_USER_FILES 目录,控制其位置用 ACTUAL_USER_FILES,控制大小用上表三个变量。官方 load-config.js schema 中没有 upload 目录键位,各类旧教程中的 ACTUAL_UPLOAD_DIR 写法对 v26.10.0 无效(未知 ACTUAL_* 变量静默忽略,不报错——排障时容易被误导)。
💡 技术拓展:为何三个上限要分开?普通上传是一次性 HTTP POST(导入文件);同步是高频小文件通道,限制更敏感;加密同步因密文有固定膨胀率(认证标签、分块填充),同样的明文预算加密后更大,所以官方把加密上限单独放宽到 50MB。若开启 E2EE 后同步大预算报 413,应调第三个变量。
八、config.json 完整配置与 CLI 配套
8.1 完整示例
/srv/actual/config.json(键名 camelCase,与第七章环境变量一一对应):
{
"port": 5006,
"hostname": "0.0.0.0",
"serverFiles": "/srv/actual/server-files",
"userFiles": "/srv/actual/user-files",
"upload": {
"fileSizeLimitMB": 25,
"fileSyncSizeLimitMB": 20,
"syncEncryptedFileSizeLimitMB": 50
},
"loginMethod": "password",
"allowedLoginMethods": ["password"]
}
逐项说明:port/hostname 收敛监听;serverFiles/userFiles 拆分数据落点(备份面见第四章);upload 组放宽导入上限到 25MB;allowedLoginMethods 只留 password,显式关闭 header/openid 攻击面(未配 OpenID 时强烈建议)。
8.2 Server CLI 配套参数
sync-server 以 NPM 包 @actual-app/sync-server 分发(要求 Node.js ≥ 22),全局安装后得到 actual-server 命令:
npm install --location=global @actual-app/sync-server
CLI 选项(官方文档口径):
| 选项 | 说明 |
|---|---|
actual-server | 以默认配置(或当前目录 config.json)启动 |
--config <path> | 指定配置文件路径 |
--reset-password | 重置服务器密码 |
-v / --version | 输出版本号 |
-h / --help | 帮助 |
环境变量与 CLI 混用示例(官方文档原文形态):
ACTUAL_DATA_DIR=./custom-directory actual-server --config ./config.json
--reset-password 的价值:password 模式忘记服务器密码时不必删库重建,一条命令交互式重置(密码哈希在 account.sqlite,该命令直接改写它)。
九、参数生效验证清单
配置变更后按此清单逐项验证(全部为实测可用命令):
# 1. 进程起来了且监听端口/地址正确 —— 看启动日志监听行
# Listening on 0.0.0.0:5007...
# 2. 健康检查通过
curl -sS http://127.0.0.1:8129/health
# {"status":"UP"}
# 3. 登录方式与引导状态符合预期
curl -sS http://127.0.0.1:8129/account/needs-bootstrap
# 4. 数据落点符合预期
find <ACTUAL_DATA_DIR> -maxdepth 2 | sort
# 5. 版本与预期一致(API 侧核对)
# getServerVersion() -> {"version":"26.10.0"}
十、常见问题(FAQ)
10.1 改了环境变量/配置不生效
排查链:确认改在"正确的层"(环境变量会覆盖 config.json,若环境里残留旧变量,改 config.json 无效)→ 确认进程真的重启了(配置仅启动时读取)→ 看启动日志监听行是否变成本次期望值 → 用 /health 与数据目录树交叉验证。实测中环境变量优先级高于 config.json(第二章回显)。
10.2 设了 ACTUAL_UPLOAD_DIR 但上传目录没变
现象:注入该变量后启动正常,但目录树里没有 uploads-dir,上传文件仍落 user-files(本章第七章实测复现)。
原因:v26.10.0 的配置 schema 中不存在该变量,未知 ACTUAL_* 变量被静默忽略。
解法:用 ACTUAL_USER_FILES(或 config.json userFiles)控制上传/预算文件落点。
10.3 全新实例登录报 invalid-password
原因与解法:服务器未 bootstrap(/account/needs-bootstrap 回显 bootstrapped:false 可确认),先走 bootstrap 设置初始密码再登录。完整回显见第五章 5.2。
10.4 OpenID 配置后登录页没有 OpenID 入口
排查链:/account/needs-bootstrap 的 availableLoginMethods 是否含 openid → ACTUAL_OPENID_* 变量是否齐全(discovery 模式四项)→ ACTUAL_OPENID_SERVER_HOSTNAME 是否用了公网可达地址(回调重定向依赖它)→ 提供方回调 URI 是否登记为 https://<服务器>/openid/callback。GitHub 类提供方需 ACTUAL_OPENID_AUTH_METHOD=oauth2。
10.5 开了端到端加密后同步大文件报 413
原因:加密同步走 ACTUAL_UPLOAD_SYNC_ENCRYPTED_FILE_SYNC_SIZE_LIMIT_MB(默认 50MB),密文膨胀后超限。
解法:调大该变量并重启进程。
10.6 多台设备连接后配置互相"污染"?
不会。sync-server 配置(端口/目录/登录方式)只属于服务器进程;客户端侧只有服务器地址与密码两个连接要素。预算文件内容的多端一致性由同步组机制(user-files 下的 group-*.sqlite 消息库)保证,与服务器配置无关。
十一、参考链接
- 官方配置总览(本文参数体系的权威来源):https://actualbudget.org/docs/config/
- 官方 OpenID 配置:https://actualbudget.org/docs/config/oauth-auth
- 官方 Server CLI 文档:https://actualbudget.org/docs/install/cli-tool
- 配置加载源码 schema:https://github.com/actualbudget/actual/blob/master/packages/sync-server/src/load-config.js
- 站内延伸阅读:《Actual Docker Compose 部署实战》(环境变量在部署编排中的应用与数据目录结构);《Actual 介绍与选型指南》(架构背景);运维命令与备份恢复见《Actual 常用命令与运维实战》篇。