如何在 VPS 上用 Docker 自托管 Actual Budget
通过 Docker Compose 在 VPS 部署 Actual Budget,了解数据卷为何决定预算是否保留、浏览器为何必须使用 HTTPS,并完成首个预算、银行导入与备份。
构建内容
Actual Budget 是一款自行托管的信封预算应用。人们寻找可以自行托管的 YNAB 替代方案时,通常会选择它。服务器只需要一个容器、一个数据卷和一个 HTTPS 名称。普通预算所需的功能在最小型 VPS 上也能稳定运行,因为服务器主要负责存储文件和同步数据。
在执行任何操作前,值得先了解其架构。预算本身是一个 SQLite 数据库,存储在浏览器和每个移动应用中。即将安装的服务器是一个同步端点:它保存账户列表、预算文件和变更日志,使手机与笔记本电脑保持一致。因此,即使服务器停机,应用仍然可以工作;只要仍有一个客户端保留副本,服务器丢失也不会导致预算丢失。
服务器需要 HTTPS 的原因
Actual 要求使用 HTTPS,这不是形式要求。浏览器只会在规范所称的安全上下文中提供 Web Crypto API。Actual 使用此接口实现端到端加密。安全上下文是 https:// 或 http://localhost。如果在另一台计算机的浏览器中从 http://203.0.113.10:5006 加载应用,这些功能将不可用,因为浏览器不会将它们提供给页面。官方移动版也会拒绝纯 http:// 服务器 URL。
因此,有两种可行的配置。将真实证书配置到真实域名前,并置于容器前端,这就是本指南采用的方式。或者使用 ACTUAL_HTTPS_KEY 和 ACTUAL_HTTPS_CERT 为服务器提供自签名证书,项目文档对此有说明,但需要在每台设备上接受浏览器警告。Let's Encrypt 提供的免费证书只需5分钟即可获取,因此应选择第一种方式。
使用 Docker Compose 安装 Actual Budget
如果服务器是全新环境,请先安装 Docker。如果您不熟悉 Compose 文件语法,请参阅 VPS 的 Docker Compose 基础知识,其中介绍了下面使用的字段。
sudo install -d -m 755 /opt/actual
sudo install -d -m 700 /opt/actual/data写入 /opt/actual/docker-compose.yml:
services:
actual:
image: actualbudget/actual-server:latest
container_name: actual
restart: unless-stopped
ports:
- '127.0.0.1:5006:5006'
volumes:
- ./data:/data该文件中有 3 个细节很重要。
镜像为 actualbudget/actual-server:latest,由项目发布到 Docker Hub,并镜像到 ghcr.io/actualbudget/actual。低功耗机器可使用 latest-alpine 标签。
容器会将所有内容写入 /data。其中包含 server-files,该目录保存带有您的登录信息和会话令牌的 account.sqlite;还包含 user-files,该目录保存预算文件本身。请挂载该路径,否则下一次 docker compose pull 会丢弃您的预算。ACTUAL_DATA_DIR 可以更改该路径,但默认值即可。
该端口仅发布到 127.0.0.1。直接使用 5006:5006 会在所有网络接口上发布端口。Docker 会在 ufw 规则之前写入自己的规则,因此即使防火墙设置为拒绝所有连接,该应用仍会暴露到互联网。Docker 发布的端口为何会绕过 ufw 解释了这一行为。绑定到 loopback 后,只有同一台服务器上的反向代理可以访问它。
启动容器:
cd /opt/actual
docker compose up --detach
docker compose logs -f actual服务器报告正在监听端口 5006 后,日志通常会稳定下来。在修改 DNS 之前,先在本地检查:
curl -fsS -o /dev/null -w '%{http_code}\n' http://127.0.0.1:5006/如果返回 200,表示应用正在提供服务。返回 curl: (7) Failed to connect 表示容器未运行,docker compose ps 会显示容器已退出。通常原因是挂载卷存在权限问题,日志中会显示 EACCES 行。
在前面配置证书和真实名称
将 A 记录指向 VPS,budget.example.com,然后等待解析生效。接着安装 nginx 并申请证书。Ubuntu 24.04 使用 nginx 配置 Certbot指南完整介绍了证书申请和续期计时器。
代理块:
server {
listen 443 ssl;
http2 on;
server_name budget.example.com;
ssl_certificate /etc/letsencrypt/live/budget.example.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/budget.example.com/privkey.pem;
client_max_body_size 100m;
location / {
proxy_pass http://127.0.0.1:5006;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}
}client_max_body_size 是人们容易忘记的配置项。完整同步时,预算文件会整体上传。Nginx 默认将请求正文大小限制为 1 MB,因此文件超过该大小后,同步会失败。nginx access 日志中会显示 413 Request Entity Too Large,而应用只会显示通用的同步错误。服务器还有独立的限制:ACTUAL_UPLOAD_FILE_SYNC_SIZE_LIMIT_MB 默认值为 20,ACTUAL_UPLOAD_SYNC_ENCRYPTED_FILE_SYNC_SIZE_LIMIT_MB 默认值为 50。因此,请将 nginx 限制设置为高于适用于您的那个值。
重新加载并测试:
sudo nginx -t && sudo systemctl reload nginx
curl -fsS -o /dev/null -w '%{http_code}\n' https://budget.example.com/首次运行:密码和第一个预算文件
在浏览器中打开 https://budget.example.com。首次打开时,页面会要求您设置服务器密码。这个密码保护整个服务器,因此请生成一个较长的随机密码,并将其保存在您之后能找到的位置,例如 自行托管的 Vaultwarden 密码管理器。无需创建用户账户。Actual 的服务器设计为使用单一密码,因此共享预算就意味着共享该密码。
然后创建预算文件。Actual 会询问是否启用端到端加密。请选择是。这样服务器只会存储密文,对于存放在租用机器上的财务数据,这是正确的选择。代价也很实际:加密密码不会发送到服务器,因此如果您丢失该密码,文件也会丢失,且无法重置。请在点击离开该页面前记下密码。
根据银行当前的余额设置起始余额,不要导入多年的历史记录。信封预算会从您当前拥有的资金开始规划,因此没有历史记录也不会造成损失。
导入交易
这里需要实事求是,而不是一味强调优点。导入体验是许多人放弃自托管预算管理的主要原因。
手动录入是基础功能,而且始终可用。对于信封预算方法,这甚至可以说就是其核心,因为录入一笔消费会让您注意到这笔支出。
文件导入可以处理大部分交易。Actual 支持读取 CSV、QIF、OFX 和 QFX,而每家银行至少会导出其中一种格式。在账户页面中按账户导入,只需映射一次列,Actual 就会记住该账户的布局。
Actual 支持自动同步银行,但需要第三方服务,因为服务器无法自行连接银行。Actual 支持北美银行使用 SimpleFIN Bridge、欧洲使用 Enable Banking、新西兰使用 Akahu,以及巴西使用 Pluggy.ai。GoCardless 仍受支持,但已不再接受新账户。您需要自行注册服务提供商的账户,生成凭据,然后将凭据添加到服务器。以 2026 年 7 月为准,SimpleFIN Bridge 每年收费 15 美元,最多支持 25 家金融机构;其他服务的定价不同。
在依赖此功能前,需要接受两个限制。API 凭据存储在服务器上,不受端到端加密保护,因为服务器必须使用这些凭据。Actual 也不会定期轮询:同步需要您按下按钮,而不是由后台任务执行。
备份,因为只有文件
您关心的所有内容都在 /opt/actual/data 下。无需导出,也无需编写脚本生成数据库转储。
唯一需要注意的是 SQLite。在服务器写入 account.sqlite 时复制它,可能会捕获未完成的事务。直到尝试恢复时,您才会发现问题。复制期间停止容器几秒钟:
cd /opt/actual
docker compose stop
restic -r sftp:backup@backup.example.com:/srv/restic backup /opt/actual/data
docker compose start按照VPS 上的 restic 备份中的方法设置计划任务。该方法涵盖存储库设置、保留策略和恢复演练。执行恢复演练。未经恢复验证的备份只是猜测。
Actual 自带的客户端备份是另一回事,也值得了解。浏览器会保留预算文件的近期副本,可从文件菜单访问。这样,即使误删了类别,也无需接触服务器即可恢复。
更新服务器
cd /opt/actual
docker compose pull
docker compose up --detachCompose 会使用新镜像重新创建容器,并重新挂载相同的卷,因此数据不会丢失。客户端也要更新。服务器版本和应用版本应保持接近。如果客户端版本比服务器版本旧很多,可能会因版本不匹配而拒绝同步。升级到主要版本前请先备份,因为迁移会在首次启动时运行,且无法降级。
出现故障时的现象
应用可以加载,但同步始终无法完成。 检查 nginx 访问日志中的 413。这表示 client_max_body_size 设置得过低。出现 502 则表示 nginx 正常运行,但容器未运行。
缺少加密选项,或移动应用拒绝该 URL。 页面不在安全上下文中。地址栏会显示带有 IP 地址或非 localhost 主机名的 http://。请修复证书,不要绕过此问题。
提示预算文件与此版本不兼容。 客户端和服务器版本不一致。将两者更新到同一发行版本,然后重新加载。
容器不断循环重启。 查看 docker compose logs actual。如果 /data 出现权限错误,表示挂载目录对容器用户不可写。如果出现地址已被占用错误,表示回环接口上的 5006 已被其他程序占用。
首次加载较慢。 打开预算文件时,整个文件都会下载到浏览器。这是一次较大的传输,之后由本地读取。问题不在于服务器规格,增加 RAM 也不会改变这一点。
FAQ
Actual Budget 是否需要 HTTPS 才能运行?
实际上需要。Actual 的端到端加密依赖浏览器的 Web Crypto API,而浏览器只会在安全上下文中提供该 API,也就是 https:// 或 http://localhost。从其他机器通过普通 HTTP 访问时,这些功能不可用,官方移动应用也会拒绝普通 HTTP 服务器 URL。请在真实主机名上使用 Let's Encrypt 证书;如果只使用桌面浏览器,也可以配合 ACTUAL_HTTPS_KEY 和 ACTUAL_HTTPS_CERT 使用自签名证书。
Actual 能否自动导入我的银行交易?
只能通过您自行注册的第三方服务导入:北美使用 SimpleFIN Bridge,欧洲使用 Enable Banking,新西兰使用 Akahu,巴西使用 Pluggy.ai。GoCardless 也受支持,但目前不再接受新账户。这些 API 凭据存储在您的服务器上,不受端到端加密保护。同步也是手动进行的,您需要按下按钮,后台不会自动轮询。导入 CSV、QIF、OFX 和 QFX 完全不需要第三方服务。
我到底需要备份什么?
备份挂载的数据目录,本指南中该目录为 /opt/actual/data。其中 server-files/account.sqlite 保存登录信息和会话,user-files 保存预算文件。复制前请停止容器,因为复制正在使用的 SQLite 数据库可能会得到不完整的写入内容。服务器上的其他位置不保存状态数据。
如果丢失加密密码,会发生什么?
无法恢复文件。密码不会传送到服务器,这正是端到端加密的目的,因此没有重置方式,也没有支持途径。创建文件后,请立即将密码保存到密码管理器中,并在不依赖同一服务器的位置保留一份副本。
Actual Budget 需要多少服务器资源?
很少。容器提供静态资源和文件,预算计算在浏览器中进行。使用 1 个共享 vCPU 和 1 GB RAM 即可稳定运行;包含数年历史记录的家庭预算,其数据目录通常只有几十 MB。磁盘压力来自备份和其他容器,而不是 Actual。