MyFin Budget Docker Compose 部署指南
MyFin Budget(myfin 10.7.0 + myfin-api 5.5.0)Docker Compose 三服务部署指南:官方镜像选型、健康检查依赖链、首次初始化、数据持久化与备份、环境变量表、升级与常见排障,全部步骤在 Ubuntu 22.04 实测通过。
MyFin Budget Docker Compose 部署指南
目标:在 Ubuntu 22.04 服务器上用 Docker Compose 从零部署 MyFin Budget(Web 前端 + REST API 后端 + MySQL 8.4 数据库),完成首次初始化、登录验证、数据持久化与备份,并给出升级与排障路径。本文所有命令与回显均在实测环境(myfin 10.7.0 / myfin-api 5.5.0 / MySQL 8.4.13)逐条验证。
一、环境信息
- 操作系统:Ubuntu 22.04 LTS x86_64
- Docker:Docker Engine 24+(含 Compose v2 插件,实测
docker compose version正常) - 软件版本:myfin(前端)10.7.0、myfin-api(后端)5.5.0(官方 ghcr.io 镜像
latest,2026-10-24 拉取;MySQL 镜像mysql:8.4实际落盘版本 8.4.13) - 组件端口:前端 80(映射宿主 8150)、API 3001(映射宿主 8151)、MySQL 3306(仅内部网络,不映射宿主)
- 数据持久化:命名数据卷
db_data(MySQL 数据目录) - 实测目录约定:compose 项目目录
/opt/myfin/,项目名myfin-stack - 官方参考:
docker-compose.yml(https://github.com/afaneca/myfin/blob/master/docker-compose.yml,2026-10-24 版本)
二、镜像选型说明
MyFin 官方发布两个应用镜像(均托管在 GitHub Container Registry),官方 compose 另配官方 MySQL:
| 服务 | 镜像 | 官方 tags | 端口 | 说明 |
|---|---|---|---|---|
| 前端 | ghcr.io/afaneca/myfin | latest | 80 | React SPA 静态产物 + nginx 托管(实测响应头 Server: nginx/1.31.6) |
| 后端 | ghcr.io/afaneca/myfin-api | latest | 3001 | Node.js + Express + Prisma REST API |
| 数据库 | mysql:8.4 | 8.4(官方 Docker Hub) | 3306 | 官方 compose 指定 8.4 线;实测落盘 8.4.13 |
关键差异与选型说明:
- 前后端为何分开成两个镜像:前端只是静态文件(构建期把 API 地址写进 JS 产物),后端承载全部业务逻辑与数据库访问;分开后可独立升级、前端可另放 CDN/反代。官方无「一体化」镜像,三服务编排是唯一官方形态。
- 数据库版本锁 8.4 而非 latest:官方 compose 显式写
mysql:8.4(MySQL 8.4 是 LTS 线),跟随官方写法可避免 MySQL 大版本漂移带来的 Prisma 兼容风险。 - tag 策略:官方 compose 两个应用镜像都用
:latest。想锁版本的读者可改用具体版本 tag(如ghcr.io/afaneca/myfin-api:5.5.0,GHCR 包页可见 tags)或 digest;本文按官方默认latest部署并在升级一节说明锁定方法。
三、前置条件
- 服务器已安装 Docker Engine 与 Compose v2 插件。验证:
docker compose version
实测回显:
Docker Compose version v2.29.7
- 服务器可访问外网拉取镜像(ghcr.io 与 Docker Hub)。若拉取缓慢,可为 Docker 守护进程配置 HTTP 代理(
/etc/docker/daemon.json或 systemd drop-in 配置HTTP_PROXY/HTTPS_PROXY后重启 docker)。 - 规划宿主端口:本文用 8150(前端)、8151(API)。确认端口未被占用:
ss -ltn | grep -E ':8150|:8151'
实测回显(无输出即未被占用):
- 目录与防火墙:创建项目目录,放行 8150/8151(如启用 ufw):
mkdir -p /opt/myfin && cd /opt/myfin
ufw allow 8150/tcp && ufw allow 8151/tcp
实测回显:
Rule added
Rule added
四、部署步骤
4.1 编写 docker-compose.yml
在 /opt/myfin/docker-compose.yml 写入以下内容(基于官方 compose,按实测环境调整了端口映射与密码,逐项含义见 6.2):
name: myfin-stack
services:
db:
image: mysql:8.4
restart: always
container_name: myfin-db
environment:
MYSQL_ROOT_PASSWORD: myfinrootpassword
MYSQL_DATABASE: myfin
MYSQL_USER: myfin
MYSQL_PASSWORD: myfinpassword
volumes:
- db_data:/var/lib/mysql
healthcheck:
test: [ "CMD", "mysqladmin", "ping", "--silent" ]
interval: 10s
timeout: 5s
retries: 10
networks:
- myfin-net
myfin-api:
image: ghcr.io/afaneca/myfin-api:latest
container_name: myfin-api
restart: unless-stopped
ports:
- "8151:3001"
environment:
# Database Configuration
- DB_NAME=myfin
- DB_USER=myfin
- DB_PW=myfinpassword
- DB_PORT=3306
- DB_HOST=db
# Email Configuration
- SMTP_HOST=
- SMTP_PORT=
- SMTP_SECURE=
- SMTP_USER=
- SMTP_PASSWORD=
- SMTP_FROM=
# Application Configuration
- PORT=3001
- LOGGING=false
- BYPASS_SESSION_CHECK=false
- ENABLE_USER_SIGNUP=false
- TRUST_PROXY=1
depends_on:
db:
condition: service_healthy
networks:
- myfin-net
myfin-frontend:
image: ghcr.io/afaneca/myfin:latest
container_name: myfin-web
restart: unless-stopped
ports:
- "8150:80"
depends_on:
myfin-api:
condition: service_healthy
environment:
- VITE_MYFIN_BASE_API_URL=http://192.168.146.128:8151
networks:
- myfin-net
volumes:
db_data:
networks:
myfin-net:
driver: bridge
ipam:
config:
- subnet: 172.30.57.0/24
gateway: 172.30.57.1
配置逐项说明(与官方 compose 的差异均已标注):
name: myfin-stack:显式 Compose 项目名,避免与其他项目同名互相down(官方未显式指定,强烈建议加上;项目名会被 Compose 规范化为小写字母数字与连字符)。MYSQL_ROOT_PASSWORD/MYSQL_USER/MYSQL_PASSWORD:数据库凭据。本文保留官方示例值便于对照,实际部署务必替换为强口令。healthcheck:官方 compose 原生自带mysqladmin ping检查;实测补充了interval/timeout/retries三个参数使健康判定更快收敛(官方默认 30 秒间隔,首次部署等待较久)。ports: 8151:3001/8150:80:官方默认映射为3001:3001、8181:80,按本机端口规划改为 8151/8150,读者按需调整。- MySQL 未映射宿主端口:官方 compose 映射了
3306:3306,仅供宿主机调试;生产建议不映射,仅走内部网络(本文已去除)。 VITE_MYFIN_BASE_API_URL=http://192.168.146.128:8151:必须改成读者自己服务器的访问地址(浏览器可达的 IP 或域名)。该地址会被注入前端 JS 产物,写错会导致前端能打开但登录无响应。networks.myfin-net:显式指定网段避免与服务器上既有网络冲突(实测曾因网段重叠报错,见 11.3)。TRUST_PROXY=1:无反代时可直接删去或设为false;取值语义详见配置参数详解篇。
4.2 拉取镜像并启动
cd /opt/myfin && docker compose up -d
实测回显(首次启动全链):
[+] Running 13/13
✔ Network myfin-stack_myfin-net Created
✔ Volume "myfin-stack_db_data" Created
✔ Container myfin-db Healthy
✔ Container myfin-api Healthy
✔ Container myfin-web Started
⚠️ 注意:首次启动 MySQL 需要初始化数据目录,
db从 Created 到 Healthy 通常需要 30–60 秒;compose 会依次等待db Healthy → myfin-api Healthy → myfin-web启动,这是官方 compose 定义的依赖链,不要手工跳过。
4.3 确认三个服务全部健康
docker compose ps
实测回显:
NAME IMAGE COMMAND SERVICE CREATED STATUS PORTS
myfin-api ghcr.io/afaneca/myfin-api:latest "docker-entrypoint.s…" myfin-api About a minute ago Up 50 seconds (healthy) 0.0.0.0:8151->3001/tcp, [::]:8151->3001/tcp
myfin-db mysql:8.4 "docker-entrypoint.s…" db About a minute ago Up About a minute (healthy) 3306/tcp, 33060/tcp
myfin-web ghcr.io/afaneca/myfin:latest "/docker-entrypoint.…" myfin-frontend About a minute ago Up 45 seconds (healthy) 0.0.0.0:8150->80/tcp, [::]:8150->80/tcp
三个服务 STATUS 均为 Up (healthy) 即部署成功。
4.4 验证 Web 与 API 可达
浏览器访问 http://<服务器IP>:8150 应出现 MyFin Budget 登录/初始化页面。命令行双验证:
curl -sI http://localhost:8150/ | head -5
实测回显:
HTTP/1.1 200 OK
Server: nginx/1.31.6
Date: Fri, 23 Oct 2026 20:46:10 GMT
Content-Type: text/html
Content-Length: 911
curl -s http://localhost:8151/
实测回显:
{"info":"MyFin API","version":"5.5.0"}
API 根路径返回版本号即后端就绪。
4.5 首次初始化(创建第一个用户)
全新实例没有任何用户,浏览器打开前端会进入初始化界面;也可直接调 API 完成初始化(与前端等效):
curl -s -X POST http://localhost:8151/setup/init \
-H 'Content-Type: application/json' \
-d '{"username":"admin","password":"q1w2e3r4","email":"admin@example.com","currency":"USD"}'
实测回显:
"Done!"
初始化成功后用该账号登录 Web(http://<服务器IP>:8150)即可开始记账。currency 可选值来自后端内置货币表(USD/EUR/CNY/GBP/JPY 等 20 余种),选定后为该实例记账本位币。
⚠️ 注意:
POST /setup/init只在「系统中没有任何用户」时可用,已有用户后再调用返回 HTTP 406(实测回显状态码406)。后续新增用户需打开ENABLE_USER_SIGNUP=true或由已有用户在界面内操作。
4.6 登录验证
curl -s -X POST http://localhost:8151/auth/ \
-H 'Content-Type: application/json' \
-d '{"username":"admin","password":"q1w2e3r4"}'
实测回显(节选):
{"user_id":1,"username":"admin","email":"admin@example.com","sessionkey":"465e8818-bc1a-485b-891d-3f9a5ad19403","sessionkey_mobile":null,"last_update_timestamp":"0","accounts":[],"currency":"USD","is_demo":false}
返回 user_id 与 sessionkey 即账号体系就绪。浏览器登录后可正常进入仪表盘(前端 API 地址注入正确时数据实时加载)。
五、数据持久化说明
- MySQL 数据:命名卷
db_data挂载到数据库容器/var/lib/mysql,重启/重建容器数据不丢。实际卷名会被项目名前缀化,实查:
docker volume ls | grep myfin
实测回显:
local myfin-stack_db_data
docker volume inspect myfin-stack_db_data --format '{{.Name}} {{.Mountpoint}}'
实测回显:
myfin-stack_db_data /var/lib/docker/volumes/myfin-stack_db_data/_data
- 应用侧配置与账本数据:除数据库外无其他持久化目录;用户上传的界面偏好存于数据库。前端/后端容器本身无状态,可随时
down/up重建。 - 备份:用 mysqldump 逻辑备份整库(实测库名 myfin):
docker exec myfin-db sh -c 'exec mysqldump -uroot -pmyfinrootpassword myfin' > /opt/myfin/myfin_backup_$(date +%F).sql
wc -c /opt/myfin/myfin_backup_*.sql
实测回显(备份文件大小随数据量变化):
32666 /opt/myfin/myfin_backup_2026-10-24.sql
恢复时反向灌入:docker exec -i myfin-db sh -c 'exec mysql -uroot -pmyfinrootpassword myfin' < 备份文件.sql。此外应用内置用户级 JSON 备份(登录后 GET /user/backup,含全部账本数据),适合单用户细粒度导出。
六、环境变量表与配置参数说明
6.1 环境变量总表
| 服务 | 变量 | 示例值/默认 | 作用 |
|---|---|---|---|
| db | MYSQL_ROOT_PASSWORD | myfinrootpassword | MySQL root 口令 |
| db | MYSQL_DATABASE | myfin | 自动创建的库名 |
| db | MYSQL_USER / MYSQL_PASSWORD | myfin / myfinpassword | 应用专用账号与口令 |
| myfin-api | DB_NAME / DB_USER / DB_PW | myfin / myfin / myfinpassword | 后端连接数据库三要素 |
| myfin-api | DB_HOST / DB_PORT | db / 3306 | 数据库地址(compose 内用服务名)与端口 |
| myfin-api | PORT | 3001 | API 监听端口 |
| myfin-api | LOGGING | false | 置 true 输出详细请求日志 |
| myfin-api | BYPASS_SESSION_CHECK | false | 置 true 跳过会话校验(仅调试用,生产禁开) |
| myfin-api | ENABLE_USER_SIGNUP | false | 是否开放自助注册 |
| myfin-api | TRUST_PROXY | 1(官方 compose) | 反向代理信任层级,详见配置参数详解篇 |
| myfin-api | SMTP_HOST/PORT/SECURE/USER/PASSWORD/FROM | 空 | 密码找回邮件发送配置(不配则无邮件找回) |
| myfin-frontend | VITE_MYFIN_BASE_API_URL | http://192.168.146.128:8151 | 浏览器访问 API 的基础地址(运行时注入生效) |
6.2 关键参数说明
- VITE_MYFIN_BASE_API_URL 是部署第一易错点:它不是「服务器内部地址」,而是浏览器访问 API 的地址。实测确认该变量在容器启动时注入前端 JS 产物即可生效(无需重新构建镜像):在浏览器下载前端主 JS 文件检索,能看到注入结果
http://192.168.146.128:8151。若写错,现象是「登录页能打开、点登录无反应/报网络错误」。 - DB_HOST=db:compose 网络内服务名即主机名,不要写 IP(容器重建 IP 会变)。
- ENABLE_USER_SIGNUP=false:官方默认关闭注册。个人自用推荐保持关闭,仅保留首个用户;开启后任何人可访问
/users注册接口自助建号。 - TRUST_PROXY=1:官方 compose 默认信任第一层代理。没有反向代理直连时建议删除该变量或设为
false,否则客户端可伪造 X-Forwarded-* 头绕过基于 IP 的限流。取值语义(数字层数/true/false/CIDR 列表)详见《MyFin Budget 配置参数详解》。
七、配置文件挂载
MyFin 的两个应用镜像均为「环境变量驱动」,没有需要挂载的主配置文件(无 myfin.conf 之类),一切配置通过 6.1 的环境变量传入。唯一需要挂载的是 MySQL 数据卷(db_data:/var/lib/mysql)。如需自定义 MySQL 服务端参数(字符集、连接数等),可追加挂载:
volumes:
- db_data:/var/lib/mysql
- ./my.cnf:/etc/mysql/conf.d/my.cnf:ro
并在项目目录创建 my.cnf(示例):
[mysqld]
character-set-server = utf8mb4
collation-server = utf8mb4_unicode_ci
max_connections = 200
修改后 docker compose up -d 重建生效。MyFin 账本字符集由 Prisma 迁移脚本管理,默认已是 utf8mb4,此挂载属可选项。
八、等价 docker run 命令
不用 Compose 时,以下三条命令等价于上述编排(按顺序执行;自定义网络模拟 compose 内部网络):
docker network create --driver bridge --subnet 172.30.57.0/24 myfin-net
docker run -d --name myfin-db --network myfin-net \
-e MYSQL_ROOT_PASSWORD=myfinrootpassword \
-e MYSQL_DATABASE=myfin \
-e MYSQL_USER=myfin -e MYSQL_PASSWORD=myfinpassword \
-v myfin-stack_db_data:/var/lib/mysql \
--health-cmd "mysqladmin ping --silent" \
mysql:8.4
docker run -d --name myfin-api --network myfin-net -p 8151:3001 \
-e DB_NAME=myfin -e DB_USER=myfin -e DB_PW=myfinpassword \
-e DB_PORT=3306 -e DB_HOST=myfin-db -e PORT=3001 \
-e LOGGING=false -e BYPASS_SESSION_CHECK=false \
-e ENABLE_USER_SIGNUP=false -e TRUST_PROXY=1 \
ghcr.io/afaneca/myfin-api:latest
docker run -d --name myfin-web --network myfin-net -p 8150:80 \
-e VITE_MYFIN_BASE_API_URL=http://192.168.146.128:8151 \
ghcr.io/afaneca/myfin:latest
⚠️ 注意:
docker run方式没有 compose 的健康检查等待编排,需确认docker inspect --format '{{.State.Health.Status}}' myfin-db为healthy后再启动 myfin-api,API 就绪后再启动前端。等价命令中数据库主机名用容器名myfin-db(compose 内是服务名db)。生产仍推荐 Compose 方式,便于一键升级与回滚。
九、常用运维命令
均在 /opt/myfin 项目目录下执行:
docker compose ps # 查看三服务状态(healthy 判定)
实测回显:
NAME IMAGE SERVICE STATUS PORTS
myfin-api ghcr.io/afaneca/myfin-api:latest myfin-api Up 12 minutes (healthy) 0.0.0.0:8151->3001/tcp
myfin-db mysql:8.4 db Up 12 minutes (healthy) 3306/tcp, 33060/tcp
myfin-web ghcr.io/afaneca/myfin:latest myfin-frontend Up 12 minutes (healthy) 0.0.0.0:8150->80/tcp
docker compose logs -f myfin-api # 跟踪后端日志(LOGGING=true 时输出请求明细)
docker compose restart myfin-api # 单独重启后端
docker compose down # 停止并移除容器(保留数据卷)
docker compose down -v # 停止并连同数据卷一起删除(危险:账本数据清空,先备份)
升级流程(官方镜像采用 latest tag 时):
docker compose pull && docker compose up -d
实测回显(无新版本时):
[+] Running 4/4
✔ db Pulled
✔ myfin-api Pulled
✔ myfin-frontend Pulled
✔ Container myfin-web Started
💡 说明:升级前先做第五节的 mysqldump 备份。数据库升级交由 Prisma 迁移在 API 容器启动时自动执行(
prisma migrate deploy),跨多个版本升级建议逐个版本跟进 Release Notes。想锁定版本,把两个应用镜像 tag 从latest改为具体版本号(如ghcr.io/afaneca/myfin-api:5.5.0、ghcr.io/afaneca/myfin:10.7.0)再up -d即可;回滚同理改回旧 tag。
十、常见问题
Q1:启动报错 invalid pool request: Pool overlaps with other one on this address space?
服务器上已有网络占用了 compose 声明的网段。实测复现与解决:把 networks.myfin-net.ipam.config.subnet 换一个不重叠的私网段(如 172.30.58.0/24),或删去 ipam 段让 Docker 自动分配;也可先 docker network ls 配合 docker network inspect <名> --format '{{range .IPAM.Config}}{{.Subnet}}{{end}}' 排查已占网段后再选。
Q2:初始化报 {"message":"\"email\" must be a valid email"}?
邮箱被后端 joi 校验拒绝。实测 admin@myfin.local 被拒(.local 非有效顶级域),改用 admin@example.com 等合法域名邮箱即可通过。
Q3:前端登录页能打开,但点登录一直无响应?
九成是 VITE_MYFIN_BASE_API_URL 写错(必须是浏览器可达地址,而非容器内部地址)。自查:浏览器直接访问 http://<服务器IP>:8151/,若能返回 {"info":"MyFin API","version":"..."} 说明 API 本身可达;再改 compose 中变量为其正确值后 docker compose up -d 重建前端。
Q4:docker compose up -d 卡在 Waiting 不动?
依赖链要求 db 与 api 先后达到 healthy。观察 docker compose ps 中 STATUS;若 db 一直 health: starting,查 docker compose logs db 是否数据卷权限/磁盘空间问题。实测正常初始化耗时 30–60 秒,超过 3 分钟再排查。
Q5:忘记 admin 密码怎么办?
配置了 SMTP 的实例可用官方找回流程(POST /auth/recovery/sendOtp 邮箱收验证码);未配置 SMTP 时用管理员思路处理:进入数据库重置,或直接以备份恢复实例后重新初始化。生产建议部署时就配好 SMTP 六变量(见 6.1)。
Q6:服务器重启后 MyFin 没有自动起来?
三个服务均已配置 restart: always/unless-stopped,Docker 服务本身开机自启(systemctl is-enabled docker)时 compose 栈会自动恢复。验证:docker compose ps 三服务 STATUS 应为 Up。
十一、参考链接
- 官方 docker-compose.yml:https://github.com/afaneca/myfin/blob/master/docker-compose.yml
- 前端镜像包页(tags 列表):https://github.com/afaneca/myfin/pkgs/container/myfin
- 后端镜像包页(tags 列表):https://github.com/afaneca/myfin-api/pkgs/container/myfin-api
- 官方 Wiki(初始设置):https://myfinbudget.com/goto/wiki-initial-setup
- 站内延伸:《MyFin Budget 介绍与选型指南》、《MyFin Budget 配置参数详解》、《MyFin Budget API 使用详解》