Ghostfolio Common — Shared Application Configuration
Ghostfolio_Common is the shared application layer for Ghostfolio. It is not
deployed on its own; instead it supplies the Ghostfolio-specific configuration that
both Ghostfolio_GKE and Ghostfolio_CloudRun
build on, so the two platform variants behave identically where it matters. End
users never configure this layer directly — it has no deployment UI inputs of its
own — but understanding what it provides explains the defaults you see in the
platform docs.
For the infrastructure that actually provisions and runs Ghostfolio, see the platform guides (Ghostfolio_GKE, Ghostfolio_CloudRun) and the foundation guides (App_GKE, App_CloudRun, App_Common).
1. What this layer provides
| Area | Provided by Ghostfolio_Common | Where it surfaces |
|---|---|---|
| Cryptographic secrets | Generates ACCESS_TOKEN_SALT and JWT_SECRET_KEY (both 32-char random strings) and stores them in Secret Manager | Injected automatically; retrieve via Secret Manager (see below) |
| Container image | Wraps the official ghostfolio/ghostfolio Docker Hub image with a custom cloud entrypoint (mirrors the Langfuse_Common technique); builds via Cloud Build | container_image output of the platform deployment |
| Database engine | Fixes Cloud SQL for PostgreSQL 15 (Prisma ORM) as the only supported engine | §Database in the platform guides |
| Database bootstrap | Defines the first-deploy job (db-init) that creates the database, role, and grants — NO separate migrate job, since Ghostfolio's own container runs Prisma migrations on every boot | initialization_jobs output |
| Object storage | None — Ghostfolio needs no bulk file/media storage | storage_buckets output (always []) |
| Core settings | Sets NODE_ENV=production and the container port (3333) | Application behaviour in the platform guides |
| Health checks | Supplies the default startup/liveness probe targeting /api/v1/health (checks DB + Redis) | §Observability in the platform guides |
2. Cryptographic secrets in Secret Manager
Two secrets are generated automatically and stored in Secret Manager — they are never set in plain text and both are boot-blocking (Ghostfolio has no sane default for either):
ACCESS_TOKEN_SALT— a 32-character random string. Used to hash the anonymous "Security Token" credential that Ghostfolio mints for the account owner on first "Get Started" (there is no seeded admin account or email/password form). Rotating it after first boot invalidates every previously issued Security Token — affected users can no longer authenticate with their existing token.JWT_SECRET_KEY— a 32-character random string. Used to sign all auth JWTs issued after Security-Token login. Rotating it immediately invalidates all active sessions, forcing every user to log back in.
Retrieve the secrets after deployment:
# List secrets for this deployment (names include the resource prefix):
gcloud secrets list --project "$PROJECT" --filter="name~access-token-salt OR name~jwt-secret-key"
# Read a secret version:
gcloud secrets versions access latest --secret=<secret-name> --project "$PROJECT"
The database password is generated and managed separately by the foundation; its
secret name is reported in the platform deployment outputs (database_password_secret).
See App_Common for the shared secret and Workload Identity model.
3. Database engine and bootstrap
Ghostfolio requires PostgreSQL 15 (Prisma ORM); the engine is fixed and MySQL
or other engines are not supported. On the first deployment a one-shot job
(db-init) runs using postgres:15-alpine and idempotently:
- Waits for PostgreSQL to be reachable,
- Creates (or updates) the application role with the generated password,
- Creates the application database if it does not already exist,
- Grants full privileges on the database and public schema,
- Signals the Cloud SQL Auth Proxy to shut down gracefully (GKE).
Unlike several other Common modules, there is no separate migrate job.
Ghostfolio's own upstream container entrypoint (docker/entrypoint.sh) runs
prisma migrate deploy, then prisma db seed, then starts the server — all on
every container boot, in the same process as the application. A failed migration
crashes the container loudly (the upstream script uses set -ex) instead of
shipping a healthy service against an empty database, which is why no additional
guard/verify job is needed here (contrast with Twenty's twenty-verify pattern).
The job is safe to re-run. Inspect the database directly with:
gcloud sql connect <instance-name> --user=<db-user> --database=<db-name> --project "$PROJECT"
4. Container image and entrypoint
The custom image is a thin wrapper FROM ghostfolio/ghostfolio:<version> — the
same technique Langfuse_Common uses for other prebuilt-image applications.
cloud-entrypoint.sh runs before Ghostfolio's own startup sequence:
- Composes
DATABASE_URLfrom the Foundation-injectedDB_*vars. Ghostfolio's DSN is a URL-authority connection string (postgresql://user:pass@host:port/db?...), so the entrypoint NEVER uses the Cloud SQL Unix-socket path (its colons would break URL-authority parsing — the same trap documented for Vikunja/Logto). It always connects over TCP usingDB_IP, branching only on whether the resolved host is loopback:- Cloud Run:
DB_IPis always the real Cloud SQL private IP →sslmode=require. - GKE:
DB_IPresolves to127.0.0.1when the cloud-sql-proxy sidecar is active →sslmode=disable.
- Cloud Run:
- Aliases
REDIS_AUTHontoREDIS_PASSWORD— the Foundation always injects the Redis password secret under the hardcoded nameREDIS_AUTH(not configurable inApp_CloudRun/App_GKE), but Ghostfolio's own Redis client readsREDIS_PASSWORD(apps/api/src/helper/redis.helper.ts).REDIS_HOSTandREDIS_PORTneed no aliasing. - Delegates to the base image's own CMD (
/ghostfolio/entrypoint.sh) viaexec "$@"— this runs Prisma migrate + seed + server start, matching the un-wrapped image's exact behaviour. The Dockerfile explicitly redeclares thisCMD— a bare customENTRYPOINTwith no matchingCMDwould otherwise silently discard it.
5. Core application settings
Ghostfolio_Common establishes the baseline Ghostfolio environment:
NODE_ENV = "production".container_port = 3333— Ghostfolio'sDEFAULT_PORT(libs/common/src/lib/config.ts); the app binds0.0.0.0(DEFAULT_HOST) by default, so no host override is needed.- Redis is required, not optional —
enable_redismust be forwarded unconditionally from the Application modules; gating it onredis_host != ""would suppress the platform's NFS-IP Redis fallback injection and leaveREDIS_HOSTunset. - No
STORAGE_TYPE/GCS wiring — Ghostfolio needs no bulk file/media storage.
6. Health probe behaviour
The default probes target GET /api/v1/health — Ghostfolio's own health
controller (apps/api/src/app/health/health.controller.ts) checks BOTH the
database AND Redis connections and returns 503 until both are healthy, so it
doubles as a genuine readiness gate rather than a simple liveness ping.
- Startup probe: HTTP, 30s initial delay, 10s period, 12-failure threshold (~2 minutes total).
- Liveness probe: HTTP, 30s initial delay, 30s period, 3-failure threshold.
7. Object storage
None. Ghostfolio needs no bulk file/media storage — storage_buckets is always
[].
gcloud storage buckets list --project "$PROJECT" # will not show a Ghostfolio-specific bucket
For the Ghostfolio-specific, user-facing configuration (variables by group, outputs, and how to explore each service from the Console and CLI), see the platform guides: Ghostfolio_GKE and Ghostfolio_CloudRun.