MyFin Budget Docker Compose 部署指南

MyFin Budget(myfin 10.7.0 + myfin-api 5.5.0)Docker Compose 三服务部署指南:官方镜像选型、健康检查依赖链、首次初始化、数据持久化与备份、环境变量表、升级与常见排障,全部步骤在 Ubuntu 22.04 实测通过。

MyFin BudgetDockerDocker Compose财务记账2026/10/24

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/myfinlatest80React SPA 静态产物 + nginx 托管(实测响应头 Server: nginx/1.31.6)
后端ghcr.io/afaneca/myfin-apilatest3001Node.js + Express + Prisma REST API
数据库mysql:8.48.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 部署并在升级一节说明锁定方法。

三、前置条件

  1. 服务器已安装 Docker Engine 与 Compose v2 插件。验证:
docker compose version

实测回显:

Docker Compose version v2.29.7
  1. 服务器可访问外网拉取镜像(ghcr.io 与 Docker Hub)。若拉取缓慢,可为 Docker 守护进程配置 HTTP 代理(/etc/docker/daemon.json 或 systemd drop-in 配置 HTTP_PROXY/HTTPS_PROXY 后重启 docker)。
  2. 规划宿主端口:本文用 8150(前端)、8151(API)。确认端口未被占用:
ss -ltn | grep -E ':8150|:8151'

实测回显(无输出即未被占用):

  1. 目录与防火墙:创建项目目录,放行 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 环境变量总表

服务变量示例值/默认作用
dbMYSQL_ROOT_PASSWORDmyfinrootpasswordMySQL root 口令
dbMYSQL_DATABASEmyfin自动创建的库名
dbMYSQL_USER / MYSQL_PASSWORDmyfin / myfinpassword应用专用账号与口令
myfin-apiDB_NAME / DB_USER / DB_PWmyfin / myfin / myfinpassword后端连接数据库三要素
myfin-apiDB_HOST / DB_PORTdb / 3306数据库地址(compose 内用服务名)与端口
myfin-apiPORT3001API 监听端口
myfin-apiLOGGINGfalse置 true 输出详细请求日志
myfin-apiBYPASS_SESSION_CHECKfalse置 true 跳过会话校验(仅调试用,生产禁开)
myfin-apiENABLE_USER_SIGNUPfalse是否开放自助注册
myfin-apiTRUST_PROXY1(官方 compose)反向代理信任层级,详见配置参数详解篇
myfin-apiSMTP_HOST/PORT/SECURE/USER/PASSWORD/FROM空密码找回邮件发送配置(不配则无邮件找回)
myfin-frontendVITE_MYFIN_BASE_API_URLhttp://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。

十一、参考链接