这是什么
Firefly III 是一套开源的记账系统,用 PHP 写的,跑在 Docker 里。我这套实例叫 Firefly,主要用来记全家的日常流水:谁花的、花在哪、从哪个账户出的。
为什么自己做
记账这件事我最怕两点:一是数据不在自己手里,二是换个工具就丢一批历史账。自建之后,整个账本就是「一个数据库 + 一个上传目录」,备份下来就是全部,迁移也不求人。顺带把 PHP 应用怎么部署、数据库怎么连、备份怎么留这三件事走了一遍,踩过的坑都留在了配置里。
解决什么问题
账户太多是记账最大的障碍:现金、银行卡、支付宝、微信各记一本,月底根本对不上。Firefly III 把它们拆成多个账户但汇总在一个视图里;报表按月份、分类、账户出,能看清钱到底花在哪儿,而不是只有一堆流水;再给几个大头类别设月度预算,超没超一眼就能看出来。
现在什么状态
运行中,已经连续记了两年家庭流水,日常记账都走网页。部署配置放在 opt-configs/ledger 下用 docker-compose 管理,容器重建不丢数据,这也是我坚持写 compose 文件而不是手工 docker run 的原因。
功能
- 多账户记账:现金、银行卡、第三方支付分开记,汇总在一处
- 报表与图表:按月、按分类、按账户出流水统计
- 预算管理:给主要开销类别设月度预算并跟踪
- Docker Compose 部署:配置集中在一个 compose 文件里,随时重建
- 数据自持:数据库与上传目录可整体备份、可迁移
技术栈
使用 / 部署教程
1. 准备目录
在装好 Docker 的主机上建工作目录,配置和数据都放这里:
mkdir -p /opt/ledger && cd /opt/ledger
2. 写 docker-compose.yml
一个应用容器 + 一个数据库容器,上传目录单独挂出来:
services:
firefly:
image: fireflyiii/core:latest
restart: unless-stopped
volumes:
- ./upload:/var/www/html/storage/upload
env_file: .env
ports:
- "8082:8080"
depends_on:
- db
db:
image: mariadb:lts
restart: unless-stopped
environment:
MYSQL_RANDOM_ROOT_PASSWORD: "yes"
MYSQL_DATABASE: firefly
MYSQL_USER: firefly
MYSQL_PASSWORD: 换成你自己的密码
volumes:
- ./db:/var/lib/mysql
3. 写 .env
关键是 APP_KEY(32 位随机字符串)和数据库连接,DB_HOST 填 compose 里的服务名 db:
APP_KEY=填32位随机字符串
DB_CONNECTION=mysql
DB_HOST=db
DB_PORT=3306
DB_DATABASE=firefly
DB_USERNAME=firefly
DB_PASSWORD=与compose里一致
4. 启动并初始化
docker compose up -d
docker compose logs -f firefly
日志里出现迁移完成后,浏览器打开 http://主机IP:8082,第一个注册的账号就是管理员,注册完记得把开放注册关掉。
5. 挂反向代理和 HTTPS
用 Nginx Proxy Manager 新建一条代理,指向 主机IP:8082,申请证书开强制 HTTPS。局域网自用可以只在内网访问;要外网用就走 Tailscale 这类内网穿透,比直接把端口暴露到公网省心。
6. 备份
两份东西都要备:数据库和 upload 目录。数据库用 dump 最稳:
docker compose exec db mysqldump -u firefly -p firefly > backup-$(date +%F).sql
tar czf upload-$(date +%F).tar.gz upload/
丢进定时任务,定期验证能不能恢复,别只备不测。
注意
APP_KEY和数据库密码要单独记一份,重建容器时必须保持一致,否则会产生一堆对不上的数据。- 备份要成对:数据库 dump 加上传目录,只备其中一个恢复出来是不完整的。
- 别把服务直接暴露在公网;真要外网访问,至少上 HTTPS + 强密码,并关闭开放注册。