Ghostfolio 3.90.0 Docker Compose 部署指南

Docker Compose 部署 Ghostfolio 3.90.0 三服务栈(app+PostgreSQL 15+Redis):环境变量、数据导入导出、持久化与备份恢复闭环实测

Ghostfolio财务记账自托管2026/10/23

Ghostfolio 3.90.0 Docker Compose 部署指南

目标:用 Docker Compose 一键部署 Ghostfolio 3.90.0 三服务栈(应用 + PostgreSQL 15 + Redis),覆盖镜像选型、环境变量、数据导入导出、持久化与「备份 → 删库 → 恢复 → API 验证」完整闭环。已在 Ubuntu 22.04(x86_64,Docker 28 / Compose v5.5.1)实测通过。

一、环境信息

  • 操作系统:Ubuntu 22.04 LTS(x86_64)
  • Docker:28.x(含 Compose v5.5.1 插件,docker compose version 验证)
  • Ghostfolio:3.90.0(官方镜像 ghostfolio/ghostfolio:3.90.0,压缩后 1.46GB;查询日期 2026-10-23)
  • PostgreSQL:15.19(镜像 postgres:15-alpine,实测 postgres --version)
  • Redis:8.10.2(镜像 redis:alpine,实测 redis-server --version)
  • 应用端口:3333(容器内),宿主机映射 3333
  • 部署目录:/opt/ghostfolio(docker-compose.yml + .env)
  • 数据目录:PostgreSQL 数据持久化目录(本篇以 bind mount 落 /opt/ghostfolio/data/postgres 为例)
  • Compose 项目名:ghostfolio-3.90.0-stack(name: 显式声明,避免与其他项目同名互扰)

二、镜像选型说明

Ghostfolio 官方在 Docker Hub 发布多架构镜像(linux/amd64、linux/arm64、linux/arm/v7),仓库 ghostfolio/ghostfolio。三服务角色与镜像选择:

服务镜像选型理由
应用ghostfolio/ghostfolio:3.90.0官方唯一镜像,tag 与 GitHub Release 一致;不建议用 latest(随发版漂移)
数据库postgres:15-alpine与官方 compose 一致;14/16 亦可,Prisma 迁移负责 schema
缓存redis:alpine仅作缓存与队列,无持久化诉求,alpine 体积最小

解读:三个镜像全部来自官方源(Docker Hub library 与 Ghostfolio 官方账号),生产固定版本 tag。拉取确认:

docker pull ghostfolio/ghostfolio:3.90.0
docker images ghostfolio/ghostfolio --format '{{.Repository}}:{{.Tag}} {{.Size}}'
docker.io/ghostfolio/ghostfolio:3.90.0
ghostfolio/ghostfolio:3.90.0   1.46GB

三、前置条件

  • 已安装 Docker Engine 与 Compose 插件(docker compose version 有输出即可);
  • 服务器可访问 Docker Hub(拉镜像);
  • 端口 3333 未被占用(ss -ltn | grep 3333 应无输出,占用则改映射左端口);
  • 规划目录 /opt/ghostfolio 用于 compose 文件与数据落盘。

四、部署步骤

4.1 准备目录与环境变量文件

mkdir -p /opt/ghostfolio/data/postgres
cd /opt/ghostfolio

创建 .env(JWT_SECRET_KEY 与 ACCESS_TOKEN_SALT 用 openssl rand -hex 32 各自生成,勿照抄演示值):

cat > /opt/ghostfolio/.env <<'EOF'
NODE_ENV=production
PORT=3333
HOST=0.0.0.0
ROOT_URL=http://<服务器IP或域名>:3333

POSTGRES_USER=ghostfolio
POSTGRES_PASSWORD=q1w2e3r4
POSTGRES_DB=ghostfolio_db
DATABASE_URL=postgresql://ghostfolio:q1w2e3r4@postgres:5432/ghostfolio_db

REDIS_HOST=redis
REDIS_PORT=6379
REDIS_PASSWORD=q1w2e3r4
REDIS_DB=0

JWT_SECRET_KEY=8f4c1a9e7b2d5f3a6c0e9d1b4a7f2c5e8d0a3b6f9c1e4d7a0b3f6c9e2d5a8f1b
ACCESS_TOKEN_SALT=a3f6c9e2d5b8f1a4c7e0d3b6a9f2c5e8d1a4b7f0c3e6d9a2b5f8c1e4d7a0b3f6
EOF

逐项说明:

  • DATABASE_URL:应用连 PostgreSQL 的连接串,主机名写服务名 postgres(Compose 内置 DNS 按服务名解析);
  • POSTGRES_USER/PASSWORD/DB:postgres 镜像初始化三要素,须与 DATABASE_URL 一致;
  • REDIS_*:Redis 分项连接(Ghostfolio 无 REDIS_URL 整串变量);REDIS_PASSWORD 在本栈同时驱动 redis 的 --requirepass;
  • ROOT_URL:对外访问地址,用于生成回调类链接,按实际域名/IP 修改;
  • 两个密钥为必填,缺失或改盐的后果见 12.2 / 12.3。

4.2 编写 docker-compose.yml

/opt/ghostfolio/docker-compose.yml(与官方 compose 同构,数据落盘改为 bind mount 并显式项目名):

name: ghostfolio-3.90.0-stack

services:
  ghostfolio:
    image: docker.io/ghostfolio/ghostfolio:3.90.0
    container_name: ghostfolio-app
    restart: unless-stopped
    init: true
    stop_grace_period: 1m
    cap_drop:
      - ALL
    security_opt:
      - no-new-privileges:true
    env_file:
      - .env
    ports:
      - "3333:3333"
    depends_on:
      postgres:
        condition: service_healthy
      redis:
        condition: service_healthy
    healthcheck:
      test: ['CMD-SHELL', 'curl -f http://localhost:3333/api/v1/health']
      interval: 10s
      timeout: 5s
      retries: 5

  postgres:
    image: docker.io/library/postgres:15-alpine
    container_name: ghostfolio-postgres
    restart: unless-stopped
    cap_drop:
      - ALL
    cap_add:
      - CHOWN
      - DAC_READ_SEARCH
      - FOWNER
      - SETGID
      - SETUID
    security_opt:
      - no-new-privileges:true
    env_file:
      - .env
    healthcheck:
      test:
        ['CMD-SHELL', 'pg_isready -d "$${POSTGRES_DB}" -U $${POSTGRES_USER}']
      interval: 10s
      timeout: 5s
      retries: 5
    volumes:
      - /opt/ghostfolio/data/postgres:/var/lib/postgresql/data

  redis:
    image: docker.io/library/redis:alpine
    container_name: ghostfolio-redis
    restart: unless-stopped
    user: '999:1000'
    cap_drop:
      - ALL
    security_opt:
      - no-new-privileges:true
    env_file:
      - .env
    command:
      - /bin/sh
      - -c
      - redis-server --requirepass "$${REDIS_PASSWORD:?REDIS_PASSWORD variable is not set}"
    healthcheck:
      test:
        ['CMD-SHELL', 'redis-cli --pass "$${REDIS_PASSWORD}" ping | grep PONG']
      interval: 10s
      timeout: 5s
      retries: 5

逐项说明:

  • name: 固定项目名,docker compose down 只作用于本项目;
  • 应用容器 cap_drop: ALL + no-new-privileges 最小权限化;init: true 让 tini 正确回收子进程;stop_grace_period: 1m 给优雅停机留时间;
  • depends_on 带 service_healthy:应用等 PostgreSQL 与 Redis 健康后才启动,pg_isready / redis-cli ping 各自守护数据库可用性;
  • postgres 保留 CHOWN 等五个能力是 alpine postgres 入口脚本修正数据目录属主所必需;
  • redis 以 999:1000 低权限用户运行,--requirepass 从 .env 注入(:? 语法在变量缺失时直接拒绝启动);
  • PostgreSQL 数据经 bind mount 落 /opt/ghostfolio/data/postgres,重建容器数据不丢。

4.3 启动栈

cd /opt/ghostfolio
docker compose up -d

实测回显(节选):

 Container ghostfolio-redis Healthy
 Container ghostfolio-postgres Healthy
 Container ghostfolio-app Started

首次启动应用容器会自动执行数据库迁移与种子(npx prisma migrate deploy → npx prisma db seed → node main,官方 entrypoint 行为),等待 10~30 秒。

确认三服务健康:

docker compose ps --format '{{.Name}}\t{{.Status}}'
ghostfolio-app    Up 25 seconds (healthy)
ghostfolio-postgres     Up 35 seconds (healthy)
ghostfolio-redis        Up 35 seconds (healthy)

4.4 验证服务

curl -s http://127.0.0.1:3333/api/v1/health
{"status":"OK"}
curl -s http://127.0.0.1:3333/api/v1/info
{"benchmarks":[],"globalPermissions":["enableAuthToken","createUserAccount"],"baseCurrency":"USD","currencies":["USD","USX"]}
curl -sI http://127.0.0.1:3333/ | head -3
HTTP/1.1 301 Moved Permanently
X-Powered-By: Express
Access-Control-Allow-Origin: *
Location: /en

浏览器打开 http://<服务器IP>:3333/(跳转 /en 注册页)创建首个账号,首个注册用户自动获得管理员角色。

4.5 注册首个账号(API 实测)

自托管 Ghostfolio 的注册即「创建用户并领取访问令牌」(无邮箱注册流程):

curl -s -X POST http://127.0.0.1:3333/api/v1/user -H 'Content-Type: application/json' -d '{"languageCode":"en"}'

实测回显(节选):

{"accessToken":"da5e8b1e580837b49ddb7e98ddb9e32104d2862da46b5fd4320df9e2e4dab851c099bfc71eb9a3fa026e016a1bdb089e29b24b2ddf73f70864aded4759e4bb0d","role":"ADMIN","authToken":"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpZCI6IjI1OTk0ODEzLTMwNGMtNGNmMy04ZGM2LWNjMjRkYjA3OTAzNSIsImlhdCI6MTc5MjczOTk3MCwiZXhwIjoxNzk1MzMxOTcwfQ.TINiCPrIvIgzpnvUFWOMVguKpKOtvs8ANvrwxyVCGn8"}
  • accessToken:128 位访问令牌,等价登录凭据,妥善保存(Web 端「登录」即输入它);
  • role: ADMIN:首个注册用户即管理员;
  • authToken:30 天有效期的 JWT,调 API 时放 Authorization: Bearer <authToken>。

五、数据导入与导出

5.1 导入交易活动(自建资产实测闭环)

Ghostfolio 支持从 CSV 导入(Web 界面)与 JSON 导入(POST /api/v1/import)。导入 JSON 的字段:type(BUY/SELL/DIVIDEND…)、symbol、dataSource、date、quantity、unitPrice、fee、currency。

默认数据源 YAHOO 需要外网访问 Yahoo Finance 行情接口做符号校验;网络受限的服务器(对端限流/防火墙)会报 symbol ("AAPL") cannot be resolved by the data source ("YAHOO")。此时改走「自建资产 + MANUAL 数据源」,实测全流程:

第一步,创建自建资产档案(符号必须 GF_ 前缀):

curl -s -X POST "http://127.0.0.1:3333/api/v1/admin/profile-data/MANUAL/GF_QZFUND1" -H "Authorization: Bearer $JWT" -H 'Content-Type: application/json' -d '{"currency":"USD"}'

实测回显(节选):

{"currency":"USD","dataGatheringFrequency":"DAILY","dataSource":"MANUAL","symbol":"GF_QZFUND1",...}

⚠️ 符号不带 GF_ 前缀会报 must start with the prefix "GF_" for the data source ("MANUAL")。

第二步,修正资产显示名(PATCH):

curl -s -X PATCH "http://127.0.0.1:3333/api/v1/admin/profile-data/MANUAL/GF_QZFUND1" -H "Authorization: Bearer $JWT" -H 'Content-Type: application/json' -d '{"name":"青竹全球ETF","assetClass":"EQUITY","assetSubClass":"ETF"}' -o /dev/null -w '%{http_code}\n'
200

第三步,导入两笔活动(买入 + 分红):

curl -s -X POST http://127.0.0.1:3333/api/v1/import -H "Authorization: Bearer $JWT" -H 'Content-Type: application/json' -d '{
  "activities": [
    {"type":"BUY","symbol":"GF_QZFUND1","dataSource":"MANUAL","date":"2025-01-15T00:00:00.000Z","quantity":100,"unitPrice":10.5,"fee":2,"currency":"USD","comment":"建仓"},
    {"type":"DIVIDEND","symbol":"GF_QZFUND1","dataSource":"MANUAL","date":"2025-06-30T00:00:00.000Z","quantity":100,"unitPrice":0.15,"fee":0,"currency":"USD"}
  ]
}'

实测回显(节选,返回入库后的活动明细):

{"activities":[{"comment":"建仓","currency":"USD","date":"2025-01-15T00:00:00.000Z","fee":2,"quantity":100,"type":"BUY","value":1050,"valueInBaseCurrency":1050},...]}

第四步,验证持仓与绩效:

curl -s "http://127.0.0.1:3333/api/v2/portfolio/performance?range=max" -H "Authorization: Bearer $JWT" | python3 -m json.tool | grep -E 'currentNetWorth|totalInvestment"'
"currentNetWorth": 1050,
"totalInvestment": 1050,

5.2 导出全量数据

curl -s "http://127.0.0.1:3333/api/v1/export" -H "Authorization: Bearer $JWT" | python3 -c "import json,sys; d=json.load(sys.stdin); print('meta:',d['meta']['version'],'activities:',len(d['activities']),'accounts:',len(d['accounts']))"

实测回显:

meta: 3.90.0 activities: 2 accounts: 1

导出 JSON 即官方导入格式(Web 界面「导出」按钮同源),可作为跨实例迁移与备份的补充手段。

六、数据持久化说明

  • PostgreSQL:唯一持久层。本栈 bind mount 到 /opt/ghostfolio/data/postgres;官方原版 compose 用命名卷 postgres:/var/lib/postgresql/data,两者二选一(bind mount 便于直接观察与物理备份,命名卷便于 compose 自管理)。实测数据目录生成:
ls /opt/ghostfolio/data/postgres | head -5
PG_VERSION
base
global
pg_wal
  • Redis:纯缓存,不挂卷,容器重建即空,无数据损失风险;
  • 应用容器:无本地状态(全在 PostgreSQL/Redis),可随时重建;
  • 重建验证:docker compose down && docker compose up -d 后登录令牌与持仓数据均保留(数据在 PostgreSQL)。注意 down 不删 bind mount 数据;若要彻底清空需另删 /opt/ghostfolio/data/postgres。

七、环境变量表

本栈 .env 全量变量(docker compose exec ghostfolio env 可复核注入结果):

变量必填示例值作用
NODE_ENV是production运行模式,官方镜像内置 production
PORT否3333应用监听端口(默认 3333)
HOST否0.0.0.0监听地址
ROOT_URL否http://<IP>:3333对外地址,用于回调链接
POSTGRES_USER是ghostfoliopostgres 初始化用户
POSTGRES_PASSWORD是q1w2e3r4postgres 初始化密码
POSTGRES_DB是ghostfolio_dbpostgres 初始化库名
DATABASE_URL是postgresql://ghostfolio:q1w2e3r4@postgres:5432/ghostfolio_db应用数据库连接串(主机写服务名)
REDIS_HOST是redisRedis 主机(服务名)
REDIS_PORT是6379Redis 端口
REDIS_PASSWORD是q1w2e3r4Redis 密码(同时驱动 requirepass)
REDIS_DB否0Redis 逻辑库索引
JWT_SECRET_KEY是32 字节 hexJWT 签名密钥
ACCESS_TOKEN_SALT是32 字节 hex访问令牌加盐,变更即全部旧令牌失效

更多可选变量(数据源 API Key、TRUST_PROXY、LOG_LEVELS、安全头开关、OIDC 等)的逐项实测说明见 《Ghostfolio 3.90.0 配置参数详解》。

八、配置文件挂载

Ghostfolio 无独立应用配置文件,全部配置经环境变量注入(env_file: .env),因此无需挂载任何配置卷。变量修改后的生效方式:

cd /opt/ghostfolio
vim .env
docker compose up -d ghostfolio

Compose 检测到 env 变更会重建应用容器(数据在 PostgreSQL,重建无损)。

九、等价 docker run

理解 compose 行为的单容器等价命令(应用容器单独运行时需接入与 postgres/redis 相同的网络):

docker network create ghostfolio-net
docker run -d --name ghostfolio-postgres --network ghostfolio-net \
  -e POSTGRES_USER=ghostfolio -e POSTGRES_PASSWORD=q1w2e3r4 -e POSTGRES_DB=ghostfolio_db \
  -v /opt/ghostfolio/data/postgres:/var/lib/postgresql/data \
  docker.io/library/postgres:15-alpine
docker run -d --name ghostfolio-redis --network ghostfolio-net \
  docker.io/library/redis:alpine redis-server --requirepass q1w2e3r4
docker run -d --name ghostfolio-app --network ghostfolio-net --env-file /opt/ghostfolio/.env \
  -p 3333:3333 --init --restart unless-stopped \
  docker.io/ghostfolio/ghostfolio:3.90.0

生产仍建议 compose 管理(健康检查与启动顺序编排是手写 docker run 做不到的)。

十、常用运维命令

docker compose ps                       # 栈内服务状态与健康
docker compose logs -f ghostfolio       # 跟踪应用日志(迁移、行情抓取都在这)
docker compose restart ghostfolio       # 重启应用
docker compose stop / start             # 停止 / 启动整栈
docker compose down                     # 删除容器与网络(保留数据目录)
docker compose pull && docker compose up -d   # 升级镜像并滚动重建

升级完整流程(固定新版本 tag):

cd /opt/ghostfolio
sed -i 's/ghostfolio:3.90.0/ghostfolio:3.91.0/' docker-compose.yml
docker compose pull
docker compose up -d

应用启动时自动执行 prisma migrate deploy,数据库 schema 随新版本自动演进;升级前务必先做第十一章的数据库备份。

十一、备份与恢复闭环(实测)

11.1 备份

cd /opt/ghostfolio
docker compose stop ghostfolio
docker exec ghostfolio-postgres pg_dump -U ghostfolio ghostfolio_db > /opt/ghostfolio/backup-ghostfolio-20261023.sql
wc -l /opt/ghostfolio/backup-ghostfolio-20261023.sql
docker compose start ghostfolio

实测回显:

1605 /opt/ghostfolio/backup-ghostfolio-20261023.sql

pg_dump 的 -U ghostfolio 指定数据库角色;导出为纯 SQL 文本,可跨 PostgreSQL 14/15/16 恢复。生产建议再加定时任务每日导出并异地保存。

11.2 模拟数据丢失并恢复

先停应用释放连接,删库重建后回灌备份:

docker compose stop ghostfolio
docker exec ghostfolio-postgres psql -U ghostfolio -d postgres -c 'DROP DATABASE ghostfolio_db;'
docker exec ghostfolio-postgres psql -U ghostfolio -d postgres -c 'CREATE DATABASE ghostfolio_db OWNER ghostfolio;'

实测回显:

DROP DATABASE
CREATE DATABASE

⚠️ 管理类 psql 命令必须带 -d postgres,否则 psql 默认连「与用户同名库」而报 FATAL: database "ghostfolio" does not exist(实测踩坑)。

回灌并重启:

docker exec -i ghostfolio-postgres psql -U ghostfolio -d ghostfolio_db < /opt/ghostfolio/backup-ghostfolio-20261023.sql
docker compose start ghostfolio

实测回显(节选):

ALTER TABLE
ALTER TABLE

11.3 恢复后验证

curl -s http://127.0.0.1:3333/api/v1/health
{"status":"OK"}

用恢复前签发的 JWT(同一用户)查绩效——净值 1050 与 5.1 导入的数据完全一致,证明用户、令牌哈希、活动数据全量还原:

curl -s "http://127.0.0.1:3333/api/v2/portfolio/performance?range=max" -H "Authorization: Bearer $JWT" | python3 -m json.tool | grep currentNetWorth
"currentNetWorth": 1050,

十二、常见问题(FAQ)

12.1 改了 PORT 后容器一直 unhealthy

现象:.env 中 PORT=4444 后应用实际监听 4444(容器内 curl localhost:4444/api/v1/health 返回 OK),但 docker compose ps 健康状态为 starting/unhealthy。原因:compose 健康检查硬编码 http://localhost:3333/api/v1/health。解决:改端口时同步修改 healthcheck 的 test 端口,或保持 3333 仅改宿主映射左端口("8150:3333")。

12.2 缺 JWT_SECRET_KEY 启动即退出

实测日志:

 Missing environment variables:
    JWT_SECRET_KEY: undefined
================================

 Exiting with error code 1

解决:.env 补齐后 docker compose up -d ghostfolio 重建。

12.3 改了 ACCESS_TOKEN_SALT 后无法登录

实测:改盐后 POST /api/v1/auth/anonymous 返回 {"statusCode":403,"message":"Forbidden"},还原盐值后旧令牌立即恢复可用。盐参与访问令牌哈希,已有数据的生产实例禁止更换。

12.4 导入活动报 symbol cannot be resolved

原因:默认数据源 YAHOO 需外网可达 Yahoo Finance;受限网络实测报 activities.0.symbol ("AAPL") cannot be resolved by the data source ("YAHOO")。解决:按 5.1 改用 MANUAL 自建资产(GF_ 前缀),或配置 CoinGecko API Key。

12.5 Redis 密码不一致

现象:应用日志刷 RedisCacheService Error ... 鉴权/连接错误。原因:.env 的 REDIS_PASSWORD 是 redis 服务 --requirepass 与应用认证的共同来源,若曾单独改过某一侧会失配。解决:统一改 .env 后 docker compose up -d(postgres/redis/app 三个服务都会以新 env 重建)。

12.6 重建后数据消失

排查顺序:确认用的是 docker compose down(保留数据目录)而非删除 /opt/ghostfolio/data/postgres;确认 volumes: 路径与首次部署一致(路径换了等于换了新库)。

十三、参考链接

  • 官方 compose 与镜像:https://github.com/ghostfolio/ghostfolio/blob/main/docker/docker-compose.yml、https://hub.docker.com/r/ghostfolio/ghostfolio
  • 官方镜像发布说明(README Self-hosting 节):查询日期 2026-10-23,三架构 amd64/arm64/arm-v7
  • 源码构建部署(非 Docker 路线):《Ubuntu 22.04 安装 Ghostfolio 3.90.0 指南》
  • REST API 导入/导出/绩效接口逐条实测:《Ghostfolio 3.90.0 API 使用详解》