IHateMoney 7.2.1 Docker Compose 部署指南
Docker Compose 部署 IHateMoney 7.2.1 官方镜像:SQLite 单容器主栈与 ihatemoney+PostgreSQL 双容器栈两套编排实测,环境变量逐项表、数据持久化、down/up 升级运维与 $$ 转义等坑位实录。
IHateMoney 7.2.1 Docker Compose 部署指南
目标:用 Docker Compose 一键部署 IHateMoney 7.2.1(多人共享记账 Web 应用)官方镜像,覆盖镜像选型、SQLite 单容器主栈与 ihatemoney + PostgreSQL 双容器栈两套编排、环境变量逐项说明、数据持久化、升级与日常运维命令。已在 Docker Engine + Docker Compose v2 环境、ihatemoney/ihatemoney:7.2.1、postgres:16-alpine 镜像实测通过(含建项目、重启持久化、PG 建表验证)。
一、环境信息
- 宿主系统:Linux x86_64(实验基线与《Ubuntu 22.04 安装 IHateMoney 7.2.1 指南》同源)
- Docker Engine:已启用(
docker compose version为 v2 系列,支持name:项目名字段与depends_on.condition: service_healthy) - IHateMoney 镜像:
ihatemoney/ihatemoney:7.2.1(Docker Hub 官方镜像,与 GitHub Releases 7.2.1 同版本号;镜像内 Python 3.11.15、gunicorn 26.0.0,实查自镜像元数据与启动日志) - 数据库镜像:
postgres:16-alpine(仅 PostgreSQL 栈需要;SQLite 主栈无需) - 端口分配:SQLite 主栈 Web
8130,PostgreSQL 栈 Web8131(均映射到容器内 8000) - compose 项目目录:
/opt/ihatemoney/compose/(下文 compose 文件、数据库 bind mount 均在此目录) - 部署形态:两套独立栈——
ihatemoney-7.2.1-stack(SQLite)与ihatemoney-7.2.1-pg-stack(PostgreSQL),各自显式项目名与独立网络,互不影响
二、镜像选型说明
| 候选镜像 | 说明 | 结论 |
|---|---|---|
ihatemoney/ihatemoney:7.2.1 | Docker Hub 官方镜像(仓库名与项目同名,官方文档指名使用),tag 精确到版本 | ✅ 本文采用 |
ihatemoney/ihatemoney:latest | 同仓库滚动标签,随上游发布漂移 | 可用但生产建议锁版本号 |
| 自建构建(仓库 Dockerfile) | 需自行维护构建与升级 | 除非要改代码,否则不必要 |
镜像关键事实(docker image inspect 实查):
- 入口为
/src/conf/entrypoint.sh,监听0.0.0.0:8000(环境变量PORT可改); - 所有配置均通过环境变量注入:entrypoint 每次启动都会用环境变量重新生成
/etc/ihatemoney/ihatemoney.cfg,随后启动 gunicorn; - 默认环境变量中
SECRET_KEY=tralala(演示值)与SESSION_COOKIE_SECURE=True,生产必须覆盖(见第六章); - 镜像内置数据库驱动:
psycopg2-binary 2.9.12(PostgreSQL)、PyMySQL 1.1.3(MySQL/MariaDB),切换后端数据库无需改镜像; - 支持
PUID/PGID环境变量:同时非 0 时,gunicorn 以对应 uid/gid 运行(bind mount 场景对齐宿主目录属主用)。
⚠️ 注意:因为 entrypoint 启动时会重写
/etc/ihatemoney/ihatemoney.cfg,不要把自定义配置文件挂载到/etc/ihatemoney/ihatemoney.cfg——会被覆盖。镜像形态下改配置的唯一正确方式是环境变量(对应关系见第六章参数表)。
三、前置条件
# 1. 宿主机已安装 Docker Engine 与 Compose v2 插件
docker --version && docker compose version
# 2. 拉取官方镜像(离线环境可先在有网机器 pull 后 docker save/load 迁移)
docker pull ihatemoney/ihatemoney:7.2.1
docker pull postgres:16-alpine # 仅 PostgreSQL 栈需要
# 3. 创建项目目录
mkdir -p /opt/ihatemoney/compose/database
实测回显(输出节选):
Docker version 29.8.1, build 4a63305
Docker Compose version v5.5.1
Status: Downloaded newer image for ihatemoney/ihatemoney:7.2.1
docker.io/ihatemoney/ihatemoney:7.2.1
生成两个部署必用值(在生产口令 q1w2e3r4 的演示约定下):
# 随机 SECRET_KEY(64 位十六进制)
python3 -c 'import secrets; print(secrets.token_hex(32))'
# 管理口令哈希(交互输入 q1w2e3r4)
docker run -it --rm --entrypoint ihatemoney ihatemoney/ihatemoney:7.2.1 generate_password_hash
实测回显(每次生成的值不同,属正常的加盐行为):
6d625d1d54514763dee0c94bf2655ae8dce023a20eebd32b6f02159d8484de24
Password:
scrypt:32768:8:1$Fp1sFH6qf2GckPTF$74ac8c4bbb52aee9e4a5fa8bbbfc9297e9e536b152c62444c03e85afa1f3b4be94b461cf2de6f8b67b8a2488affb5c5de3bfa4ab599e5fb978978a14f9aefcae
四、部署步骤
4.1 主栈:SQLite 单容器(推荐入门/小团队)
编写 docker-compose.yml:
name: ihatemoney-7.2.1-stack
services:
ihatemoney:
image: ihatemoney/ihatemoney:7.2.1
container_name: ihatemoney-7.2.1-web
restart: unless-stopped
ports:
- "8130:8000"
environment:
SECRET_KEY: "6d625d1d54514763dee0c94bf2655ae8dce023a20eebd32b6f02159d8484de24"
SQLALCHEMY_DATABASE_URI: "sqlite:////database/ihatemoney.db"
ACTIVATE_DEMO_PROJECT: "False"
ACTIVATE_ADMIN_DASHBOARD: "True"
# 管理口令为 q1w2e3r4 的 scrypt 哈希;compose 中 $ 必须写作 $$
ADMIN_PASSWORD: "scrypt:32768:8:1$$Fp1sFH6qf2GckPTF$$74ac8c4bbb52aee9e4a5fa8bbbfc9297e9e536b152c62444c03e85afa1f3b4be94b461cf2de6f8b67b8a2488affb5c5de3bfa4ab599e5fb978978a14f9aefcae"
ALLOW_PUBLIC_PROJECT_CREATION: "True"
SESSION_COOKIE_SECURE: "False"
BABEL_DEFAULT_TIMEZONE: "Asia/Shanghai"
ENABLE_CAPTCHA: "False"
SHOW_ADMIN_EMAIL: "True"
MAIL_DEFAULT_SENDER: "Budget manager <admin@example.com>"
volumes:
- ./database:/database
networks:
- ihm-net
networks:
ihm-net:
name: ihatemoney-compose-net
driver: bridge
ipam:
config:
- subnet: 10.89.59.0/24
逐项说明:name: 显式声明 compose 项目名,避免与其他部署同名互相干扰;ports 把容器 8000 映射到宿主 8130(按宿主实际空闲端口调整);ADMIN_PASSWORD 中哈希含 $,在 compose 文件里必须写成 $$,否则 Compose 会把 $Fp1sFH6qf2GckPTF 当变量插值替换成空(官方文档专门提醒此坑);volumes 把宿主 ./database 挂到容器 /database(镜像默认 SQLite 路径即 sqlite:////database/ihatemoney.db);网络显式命名并指定子网,避免与宿主上其他 compose 项目网段冲突。
启动并查看状态:
cd /opt/ihatemoney/compose
docker compose up -d
docker compose ps --format 'table {{.Name}}\t{{.Image}}\t{{.Status}}\t{{.Ports}}'
实测回显:
Container ihatemoney-7.2.1-web Created
Container ihatemoney-7.2.1-web Starting
Container ihatemoney-7.2.1-web Started
NAME IMAGE STATUS PORTS
ihatemoney-7.2.1-web ihatemoney/ihatemoney:7.2.1 Up 4 seconds 0.0.0.0:8130->8000/tcp, [::]:8130->8000/tcp
启动日志(首次启动会看到运行用户 uid/gid 与 gunicorn 启动信息):
docker compose logs --no-log-prefix ihatemoney | head -5
实测回显:
User uid: 0
User gid: 0
[2026-10-23 11:20:47 +0000] [1] [INFO] Starting gunicorn 26.0.0
数据库文件自动落盘到 bind mount(首次访问时应用自动建库迁移):
ls -la database/
实测回显:
total 152
drwxr-xr-x 2 root root 4096 10月 23 19:20 .
drwxr-xr-x 3 root root 4096 10月 23 19:20 ..
-rw-r--r-- 1 root root 143360 10月 23 19:20 ihatemoney.db
验证 Web 与 API(创建测试项目,HTTP 201 即成功):
curl -s -o /dev/null -w "home HTTP %{http_code}\n" http://127.0.0.1:8130/
curl -s -o /dev/null -w "create-project HTTP %{http_code}\n" -X POST http://127.0.0.1:8130/api/projects -d 'name=宿舍AA账本&id=dorm2026&password=q1w2e3r4&contact_email=admin@example.com'
实测回显:
home HTTP 200
create-project HTTP 201
浏览器访问 http://<宿主机IP>:8130/,用项目标识 dorm2026、私有码 q1w2e3r4 即可登录测试项目;管理仪表盘入口 /admin 用口令 q1w2e3r4 进入。
4.2 变体栈:ihatemoney + PostgreSQL 双容器
需要外部数据库(多人运维/已有 PG 设施)时,用双容器编排。编写 docker-compose.postgres.yml:
name: ihatemoney-7.2.1-pg-stack
services:
ihatemoney:
image: ihatemoney/ihatemoney:7.2.1
container_name: ihatemoney-7.2.1-pg-web
restart: unless-stopped
ports:
- "8131:8000"
environment:
SECRET_KEY: "6d625d1d54514763dee0c94bf2655ae8dce023a20eebd32b6f02159d8484de24"
SQLALCHEMY_DATABASE_URI: "postgresql://ihatemoney:q1w2e3r4@postgres:5432/ihatemoney"
ACTIVATE_DEMO_PROJECT: "False"
ACTIVATE_ADMIN_DASHBOARD: "True"
ADMIN_PASSWORD: "scrypt:32768:8:1$$Fp1sFH6qf2GckPTF$$74ac8c4bbb52aee9e4a5fa8bbbfc9297e9e536b152c62444c03e85afa1f3b4be94b461cf2de6f8b67b8a2488affb5c5de3bfa4ab599e5fb978978a14f9aefcae"
SESSION_COOKIE_SECURE: "False"
BABEL_DEFAULT_TIMEZONE: "Asia/Shanghai"
depends_on:
postgres:
condition: service_healthy
networks:
- ihm-pg-net
postgres:
image: postgres:16-alpine
container_name: ihatemoney-7.2.1-pg-db
restart: unless-stopped
environment:
POSTGRES_USER: "ihatemoney"
POSTGRES_PASSWORD: "q1w2e3r4"
POSTGRES_DB: "ihatemoney"
volumes:
- ./pgdata:/var/lib/postgresql/data
healthcheck:
test: ["CMD-SHELL", "pg_isready -U ihatemoney -d ihatemoney"]
interval: 5s
timeout: 3s
retries: 10
networks:
- ihm-pg-net
networks:
ihm-pg-net:
name: ihatemoney-pg-net
driver: bridge
ipam:
config:
- subnet: 10.89.61.0/24
逐项说明:两个 service 通过 depends_on + pg_isready 健康检查保证数据库就绪后应用才启动(IHateMoney 启动即连库迁移,数据库没就绪会启动失败);连接串用服务名 postgres 作主机名(同一 compose 网络内置 DNS 解析);./pgdata 持久化 PG 数据目录。
docker compose -f docker-compose.postgres.yml up -d
docker compose -f docker-compose.postgres.yml ps --format 'table {{.Name}}\t{{.Image}}\t{{.Status}}'
实测回显:
Container ihatemoney-7.2.1-pg-db Healthy
Container ihatemoney-7.2.1-pg-web Starting
Container ihatemoney-7.2.1-pg-web Started
NAME IMAGE STATUS
ihatemoney-7.2.1-pg-db postgres:16-alpine Up 11 seconds (healthy)
ihatemoney-7.2.1-pg-web ihatemoney/ihatemoney:7.2.1 Up 6 seconds
验证(建项目 + 进 PG 容器核对建表):
curl -s -o /dev/null -w "pg-stack home HTTP %{http_code}\n" http://127.0.0.1:8131/
curl -s -o /dev/null -w "pg-stack create HTTP %{http_code}\n" -X POST http://127.0.0.1:8131/api/projects -d 'name=旅行基金&id=trip2026&password=q1w2e3r4&contact_email=admin@example.com'
docker exec ihatemoney-7.2.1-pg-db psql -U ihatemoney -d ihatemoney -c '\dt'
实测回显(建表输出节选):
pg-stack home HTTP 200
pg-stack create HTTP 201
List of relations
Schema | Name | Type | Owner
--------+-------------------+-------+------------
public | alembic_version | table | ihatemoney
public | archive | table | ihatemoney
public | bill | table | ihatemoney
public | bill_version | table | ihatemoney
public | billowers | table | ihatemoney
public | billowers_version | table | ihatemoney
public | person | table | ihatemoney
public | person_version | table | ihatemoney
public | project | table | ihatemoney
4.3 两栈对比与选择建议
| 维度 | SQLite 主栈 | PostgreSQL 变体栈 |
|---|---|---|
| 容器数 | 1 | 2(应用 + 数据库) |
| 数据落盘 | ./database/ihatemoney.db 单文件 | ./pgdata/ 目录 |
| 运维复杂度 | 低(无额外依赖) | 中(需维护 PG 口令/升级/备份) |
| 适用规模 | 家庭/小团队,几十个项目内 | 多人协作、已有 PG 设施、需外部备份体系 |
| 连接串 | sqlite:////database/ihatemoney.db | postgresql://用户:口令@postgres:5432/ihatemoney |
选择建议:个人/家庭自托管直接 SQLite 主栈;多人生产、需要专业备份与并发写入再上 PG 栈。切 MySQL/MariaDB 只需把连接串改为 mysql+pymysql://用户:口令@主机:3306/库名(镜像已内置 PyMySQL 驱动)。
五、数据持久化说明
- SQLite 栈:全部数据在一个文件
./database/ihatemoney.db(bind mount 到容器/database)。实测重启后数据完好——
docker compose restart
sleep 3
curl -s --basic -u dorm2026:q1w2e3r4 http://127.0.0.1:8130/api/projects/dorm2026 -o /dev/null -w "after-restart login HTTP %{http_code}\n"
实测回显:
Container ihatemoney-7.2.1-web Started
after-restart login HTTP 200
- PG 栈:数据在
./pgdata/;备份用docker exec ihatemoney-7.2.1-pg-db pg_dump -U ihatemoney ihatemoney > ihm-$(date +%F).sql。 - 迁移(换宿主机):SQLite 栈打包
docker compose down后整个 compose 目录(含 database/)拷贝到新机重新up -d即可。 - 权限:容器默认以 root(uid 0)写入 bind mount(实测
ihatemoney.db属主为 root)。若宿主要求非 root 属主,在两个栈的 ihatemoney 服务上加PUID/PGID环境变量(如PUID: "1000"、PGID: "1000")并把宿主目录chown成对应 uid/gid。
六、环境变量表(镜像全部可用参数)
以下为镜像 entrypoint 支持的全部环境变量(实查自镜像默认 Env 与 entrypoint.sh,值列标注镜像默认值):
| 环境变量 | 镜像默认值 | 建议生产值 | 说明 |
|---|---|---|---|
SECRET_KEY | tralala | 64 位随机串 | ⚠️ 默认是公开演示值,必须覆盖;泄露可伪造会话登录任意项目 |
SQLALCHEMY_DATABASE_URI | sqlite:////database/ihatemoney.db | 同默认或 PG/MySQL 连接串 | 数据库连接串 |
DEBUG | False | False | 调试模式,生产禁开 |
ACTIVATE_ADMIN_DASHBOARD | False | True(配合口令) | 管理仪表盘开关 |
ACTIVATE_DEMO_PROJECT | True | False | 首页公共演示项目,私有实例关闭 |
ADMIN_PASSWORD | 空 | scrypt 哈希 | 管理口令哈希;compose 里 $ 写 $$ |
ALLOW_PUBLIC_PROJECT_CREATION | True | 按需 | 匿名建项目开关,收口则 False(建项目需管理口令) |
BABEL_DEFAULT_TIMEZONE | UTC | Asia/Shanghai | 界面显示时区 |
SESSION_COOKIE_SECURE | True | HTTPS 保持 True;纯 HTTP 部署 False | 纯 HTTP 下保持默认会导致浏览器丢会话 Cookie |
SHOW_ADMIN_EMAIL | True | 按需 | 错误页是否展示管理员邮箱 |
APPLICATION_ROOT | / | /(子路径部署改) | 应用挂载路径 |
ENABLE_CAPTCHA | False | 公网实例 True | 建项目验证码 |
LEGAL_LINK | 空 | 按需 | 法律/隐私页 URL |
MAIL_DEFAULT_SENDER | Budget manager <admin@example.com> | 真实发件人 | 发件人展示 |
MAIL_SERVER | localhost | 外部 SMTP 主机 | 邮件服务器(邀请/找回邮件用) |
MAIL_PORT | 25 | 465(SSL)/587(TLS) | SMTP 端口 |
MAIL_USE_SSL | False | 465 端口时 True | SSL 直连 |
MAIL_USE_TLS | False | 587 端口时 True | STARTTLS |
MAIL_USERNAME | 空 | SMTP 账号 | 认证账号 |
MAIL_PASSWORD | 空 | SMTP 口令 | 认证口令(敏感,勿入库) |
PUID / PGID | 0 / 0 | bind mount 对齐属主时设置 | 以非 root 运行 gunicorn |
配置文件挂载方式(与原生部署的差异):镜像不支持挂载 /etc/ihatemoney/ihatemoney.cfg(启动时被 entrypoint 重写);所有配置以环境变量为准。若确需复杂配置文件(如含邮件全套参数),可把文件挂到其他路径并额外注入 IHATEMONEY_SETTINGS_FILE_PATH 指向它——但注意该变量必须在 entrypoint 生成配置之后对应用生效,因此更稳妥的做法仍是一律用环境变量。
等价 docker run 命令(与 4.1 主栈等价,便于对照理解或临时起测试实例):
docker run -d --name ihatemoney-7.2.1-web \
-p 8130:8000 \
-e SECRET_KEY="6d625d1d54514763dee0c94bf2655ae8dce023a20eebd32b6f02159d8484de24" \
-e SQLALCHEMY_DATABASE_URI="sqlite:////database/ihatemoney.db" \
-e ACTIVATE_DEMO_PROJECT=False \
-e ACTIVATE_ADMIN_DASHBOARD=True \
-e ADMIN_PASSWORD='scrypt:32768:8:1$Fp1sFH6qf2GckPTF$74ac8c4bbb52aee9e4a5fa8bbbfc9297e9e536b152c62444c03e85afa1f3b4be94b461cf2de6f8b67b8a2488affb5c5de3bfa4ab599e5fb978978a14f9aefcae' \
-e SESSION_COOKIE_SECURE=False \
-e BABEL_DEFAULT_TIMEZONE="Asia/Shanghai" \
-v /opt/ihatemoney/compose/database:/database \
ihatemoney/ihatemoney:7.2.1
⚠️ 注意
docker run形式里 ADMIN_PASSWORD 的$不需要$$转义(那是 Compose 的变量插值规则,docker run 直接传给 shell 时用单引号保护即可)。
七、常用运维命令
cd /opt/ihatemoney/compose
# 查看状态/资源占用
docker compose ps
docker compose top
实测回显(docker compose top,可见容器内 gunicorn 主/工作进程以 root 运行、监听 0.0.0.0:8000):
SERVICE # UID PID PPID C STIME TTY TIME CMD
ihatemoney 1 root 1409953 1409925 1 19:24 ? 00:00:00 /usr/local/bin/python3.11 /usr/local/bin/gunicorn ihatemoney.wsgi:application -b 0.0.0.0:8000 --log-syslog
ihatemoney 1 root 1410135 1409953 3 19:24 ? 00:00:00 /usr/local/bin/python3.11 /usr/local/bin/gunicorn ihatemoney.wsgi:application -b 0.0.0.0:8000 --log-syslog
# 日志(实时跟踪)
docker compose logs -f --tail 100 ihatemoney
# 重启应用容器
docker compose restart
# 停止与销毁(SQLite 栈;PG 栈把 -f 换成 docker-compose.postgres.yml)
docker compose down # 保留 bind mount 数据
docker compose down -v # 连同 named volume 一起删(本文栈未用 named volume,等价上一条)
# 升级版本(锁版本 tag 的改法)
docker pull ihatemoney/ihatemoney:7.2.2 # 以官方 Releases 实际最新版为准
# 把 compose 文件中的 image tag 改为新版本后:
docker compose up -d
实测回显(docker compose down 后再 up -d 的完整循环,已验证 down/up 后登录接口正常 HTTP 200):
Container ihatemoney-7.2.1-web Removing
Container ihatemoney-7.2.1-web Removed
Network ihatemoney-compose-net Removing
Network ihatemoney-compose-net Removed
Container ihatemoney-7.2.1-web Created
Container ihatemoney-7.2.1-web Starting
Container ihatemoney-7.2.1-web Started
💡 说明:镜像内应用启动即自动执行数据库迁移(首访时建表/补迁移),升级版本无需手动跑
db upgrade;但升级前请按第五章备份 SQLite 文件或 pg_dump,回滚时才有的退。
八、常见问题(FAQ)
Q1:登录后一刷新就退回登录页?
原因:镜像默认 SESSION_COOKIE_SECURE=True,纯 HTTP 部署时浏览器拒发 Cookie。解决:compose 的 environment 里显式设 SESSION_COOKIE_SECURE: "False"(本文 4.1 主栈已按此配置);HTTPS 部署则保持默认并在前面加 TLS 代理。
Q2:管理仪表盘 /admin 无法进入?
排查两处:ACTIVATE_ADMIN_DASHBOARD 是否为 True、ADMIN_PASSWORD 是否为非空哈希(两者任一为默认值时管理面禁用)。另外检查哈希里的 $ 是否已写成 $$——若漏写,Compose 插值后 ADMIN_PASSWORD 实际为空串,管理面即禁用。可用以下命令核对实际生效值(本文实测,$$ 正确转义后容器内为单 $ 的完整哈希):
docker compose exec ihatemoney grep ADMIN_PASSWORD /etc/ihatemoney/ihatemoney.cfg
实测回显(截断显示):
ADMIN_PASSWORD = 'scrypt:32768:8:1$Fp1sFH6qf2GckPTF$74ac8c4bbb52aee9e4a5fa8bbbfc9297e9e536b152c62444
Q3:bind mount 里的数据库文件属主是 root,宿主程序读不了?
容器默认以 root 运行。解决:按第五章加 PUID/PGID 并对齐宿主目录属主后 docker compose up -d 重建容器。
Q4:想在 ihatemoney 后面接 Nginx/HTTPS 怎么做?
把 4.1 主栈的 ports 改为只绑本机("127.0.0.1:8130:8000"),宿主 Nginx 反代到 http://127.0.0.1:8130(站点配置可参考《Ubuntu 22.04 安装 IHateMoney 7.2.1 指南》4.5 节的 Nginx 模板,proxy_pass 目标换成本机端口);上 HTTPS 后把 SESSION_COOKIE_SECURE 改回 True。
Q5:升级后页面报数据库错误?
个别版本迁移在升级后首次请求时进行,刷新或重启容器(docker compose restart)后自动补齐;仍异常时查 docker compose logs ihatemoney 中的 alembic 报错行,必要时回滚镜像 tag 并恢复第五章备份的数据库。
九、参考链接
- 官方 Docker 安装文档:https://ihatemoney.readthedocs.io/en/latest/installation.html(Docker 节)
- 官方镜像 Docker Hub:https://hub.docker.com/r/ihatemoney/ihatemoney
- GitHub Releases(版本实查):https://github.com/spiral-project/ihatemoney/releases
- 本专栏其他篇目:《IHateMoney 7.2.1 介绍与选型指南》《Ubuntu 22.04 安装 IHateMoney 7.2.1 指南》《IHateMoney 7.2.1 配置参数详解》《IHateMoney 7.2.1 API/CLI 使用详解》