Ghostfolio
Ghostfolio is an open-source wealth-management application for tracking portfolios, holdings, activities and net worth. The HelmForge chart deploys the official Ghostfolio image with PostgreSQL, Redis and security-oriented Kubernetes defaults.
The chart is beta. Ghostfolio performs database migrations, seed work and scheduled portfolio jobs in the application
process, so this release deliberately runs one replica with a Recreate rollout.
What this chart provides
- official
docker.io/ghostfolio/ghostfolio:3.82.0release image; - maintained PostgreSQL and Redis dependencies, with contracts for externally managed services;
- upstream-native startup, liveness and dependency-aware readiness probes;
- retained application identity, existing Secret support and native External Secrets Operator resources;
- optional experimental OIDC authentication;
- opt-in Ingress or Gateway API exposure;
- non-root execution, read-only root filesystem, dropped capabilities and no mounted API token;
- product-aware NetworkPolicy, dual-stack Service and standard scheduling controls.
The chart does not claim application-level horizontal scaling, Prometheus metrics or automated backups because those contracts are not documented by upstream.
Install
helm repo add helmforge https://repo.helmforge.dev
helm repo update
helm install ghostfolio helmforge/ghostfolio \
--namespace ghostfolio \
--create-namespace
The default installation deploys PostgreSQL and Redis with persistent volumes. The first account registered in a new Ghostfolio instance becomes its administrator. Keep external exposure private until that registration is complete.
Understand the pinned image
The default image is the official Ghostfolio 3.82.0 release. Its manifest was verified for Linux amd64, arm/v7
and arm64. Changing image.tag remains available for a reviewed upgrade or private mirror.
Expose the application
Ingress and Gateway API support are opt-in and mutually exclusive. Set the canonical public origin explicitly in production so generated links and OIDC callbacks are stable:
ghostfolio:
rootUrl: https://portfolio.example.com
trustProxy: '1'
ingress:
enabled: true
ingressClassName: nginx
hosts:
- host: portfolio.example.com
paths:
- path: /
pathType: Prefix
tls:
- secretName: portfolio-tls
hosts:
- portfolio.example.com
The chart can derive ROOT_URL from the first Ingress host or HTTPRoute hostname, but an explicit HTTPS value avoids
ambiguity. Set ghostfolio.trustProxy only to the trust policy required by the selected ingress path.
Use managed PostgreSQL and Redis
For production, disable the bundled services and reference credentials held in Kubernetes Secrets. Database URLs must be complete and URL-encoded because this avoids exposing passwords in Helm values or assembling unsafe connection strings in templates.
postgresql:
enabled: false
database:
external:
enabled: true
existingSecret: ghostfolio-database
urlKey: database-url
directUrlEnabled: true
directUrlKey: direct-url
host: postgres.database.svc.cluster.local
networkPolicyPeers:
- namespaceSelector:
matchLabels:
kubernetes.io/metadata.name: database
redis:
enabled: false
external:
enabled: true
host: redis.cache.svc.cluster.local
existingSecret: ghostfolio-redis
passwordKey: redis-password
networkPolicyPeers:
- namespaceSelector:
matchLabels:
kubernetes.io/metadata.name: cache
Use DIRECT_URL when migrations must bypass a transaction pooler. Both service hosts are also used by the dependency
wait containers. Configure matching NetworkPolicy peers for private destinations.
Preserve application identity
The chart creates and retains random values for ACCESS_TOKEN_SALT and JWT_SECRET_KEY by default. For deterministic
GitOps or recovery, create a Secret containing access-token-salt and jwt-secret-key, then set
ghostfolio.existingSecret. Rotating either value invalidates authentication material.
Market-data provider credentials can be supplied with ghostfolio.extraEnv using valueFrom.secretKeyRef, or through
ghostfolio.extraEnvFrom. Do not store credentials directly in values files.
Configure OIDC
Ghostfolio marks OIDC as experimental. Register the callback
https://portfolio.example.com/api/auth/oidc/callback, store the client secret separately, and configure:
ghostfolio:
rootUrl: https://portfolio.example.com
oidc:
enabled: true
issuer: https://identity.example.com/realms/portfolio
clientId: ghostfolio
existingSecret: ghostfolio-oidc
clientSecretKey: client-secret
scopes:
- openid
- profile
- email
Test login, logout, callback routing and account mapping with the pinned Ghostfolio version before depending on OIDC in production.
Materialize Secrets with External Secrets Operator
externalSecrets.items accepts native ExternalSecret specifications. The operator and a SecretStore or
ClusterSecretStore must already exist. This example supplies application identity:
externalSecrets:
enabled: true
items:
- name: application
fullnameOverride: ghostfolio-application
spec:
secretStoreRef:
name: production-secrets
kind: ClusterSecretStore
target:
name: ghostfolio-application
data:
- secretKey: access-token-salt
remoteRef:
key: ghostfolio/application
property: access-token-salt
- secretKey: jwt-secret-key
remoteRef:
key: ghostfolio/application
property: jwt-secret-key
ghostfolio:
existingSecret: ghostfolio-application
Use the same contract for OIDC, database, Redis and provider credentials.
Health and operations
Startup and liveness use /api/v1/health/liveness, which does not couple process restarts to PostgreSQL or Redis
outages. Readiness uses /api/v1/health and withdraws traffic when either dependency is unavailable. Keep the readiness
timeout at five seconds or longer because the upstream Redis check can consume that interval.
The application waits for both TCP listeners, then its official entrypoint runs Prisma migrations and seed work before
starting the NestJS server. Keep replicaCount=1; use one Helm release per independent Ghostfolio instance.
Network policy
The default policy allows namespace-local HTTP ingress, DNS, the bundled PostgreSQL and Redis workloads, and public
HTTP/HTTPS egress for market-data providers. Public egress excludes private and special-use ranges. External private
services require database.external.networkPolicyPeers, redis.external.networkPolicyPeers or explicit
networkPolicy.extraEgress rules.
If an ingress controller runs in another namespace, add its namespace and pod selectors to networkPolicy.ingressFrom.
Back up and recover
PostgreSQL is the system of record. Redis persistence helps cache continuity but is not a substitute for a PostgreSQL
backup. The chart is marked backup: false because it does not ship an automated end-to-end backup controller.
Before an upgrade, create a transactionally consistent PostgreSQL backup, preserve the application Secret and record the chart and image versions. Restore into an isolated namespace and verify login, holdings, account history and a provider refresh before declaring the recovery procedure successful.
Configuration reference
Image, identity and application
| Value | Default | Purpose |
|---|---|---|
nameOverride |
"" |
Override the chart name in resource names |
fullnameOverride |
"" |
Override the complete release resource name |
commonLabels |
{} |
Labels added to every chart-owned resource |
replicaCount |
1 |
Enforced singleton migration and cron owner |
image.repository |
docker.io/ghostfolio/ghostfolio |
Official application repository |
image.tag |
3.82.0 |
Pinned official application release |
image.pullPolicy |
IfNotPresent |
Kubernetes image pull policy |
imagePullSecrets |
[] |
Registry credential references |
ghostfolio.rootUrl |
"" |
Canonical origin; empty derives it from HTTP exposure |
ghostfolio.trustProxy |
"" |
Express trusted-proxy setting |
ghostfolio.authTokenEnabled |
true |
Enable token authentication |
ghostfolio.cronEnabled |
true |
Enable scheduled gathering and snapshots |
ghostfolio.securityHeadersEnabled |
false |
Enable experimental upstream security headers |
ghostfolio.mcpEnabled |
false |
Enable the experimental /mcp endpoint |
ghostfolio.rateLimitingEnabled |
false |
Enable upstream rate limiting |
ghostfolio.readOnlyModeEnabled |
false |
Prevent application changes |
ghostfolio.logLevels |
[] |
NestJS log levels; empty keeps the upstream default |
ghostfolio.existingSecret |
"" |
Secret containing stable application identity |
ghostfolio.accessTokenSaltKey |
access-token-salt |
Secret key for ACCESS_TOKEN_SALT |
ghostfolio.jwtSecretKey |
jwt-secret-key |
Secret key for JWT_SECRET_KEY |
ghostfolio.extraEnv |
[] |
Additional advanced environment entries |
ghostfolio.extraEnvFrom |
[] |
Additional Secret or ConfigMap environment sources |
OIDC and data services
| Value | Default | Purpose |
|---|---|---|
oidc.enabled |
false |
Enable experimental OpenID Connect |
oidc.issuer |
"" |
HTTPS discovery issuer |
oidc.clientId |
"" |
Registered client identifier |
oidc.existingSecret |
"" |
Secret containing the client secret |
oidc.clientSecretKey |
client-secret |
OIDC client-secret key |
oidc.scopes |
[openid] |
Requested OIDC scopes |
oidc.callbackUrl |
"" |
Explicit callback URL override |
oidc.authorizationUrl |
"" |
Manual authorization endpoint override |
oidc.tokenUrl |
"" |
Manual token endpoint override |
oidc.userInfoUrl |
"" |
Manual user-info endpoint override |
database.external.enabled |
false |
Use externally managed PostgreSQL |
database.external.existingSecret |
"" |
Secret containing complete database URLs |
database.external.urlKey |
database-url |
DATABASE_URL Secret key |
database.external.directUrlKey |
direct-url |
DIRECT_URL Secret key |
database.external.directUrlEnabled |
false |
Consume the direct migration URL |
database.external.host |
"" |
Host used for dependency waiting and policy context |
database.external.port |
5432 |
External PostgreSQL port |
database.external.networkPolicyPeers |
[] |
Allowed private PostgreSQL peers |
postgresql.enabled |
true |
Deploy bundled PostgreSQL |
postgresql.architecture |
standalone |
PostgreSQL topology |
postgresql.auth.database |
ghostfolio |
Initial application database |
postgresql.auth.username |
ghostfolio |
Application database user |
postgresql.auth.password |
"" |
Application password; empty generates one |
postgresql.auth.postgresPassword |
"" |
Administrator password; empty generates one |
postgresql.standalone.persistence.enabled |
true |
Persist PostgreSQL data |
postgresql.standalone.persistence.size |
8Gi |
PostgreSQL PVC request |
postgresql.standalone.resources |
requests and limits | PostgreSQL compute allocation |
redis.enabled |
true |
Deploy bundled Redis |
redis.architecture |
standalone |
Redis topology |
redis.auth.enabled |
true |
Require Redis authentication |
redis.auth.password |
"" |
Redis password; empty generates one |
redis.standalone.persistence.enabled |
true |
Persist Redis state |
redis.standalone.persistence.size |
1Gi |
Redis PVC request |
redis.standalone.resources |
requests and limits | Redis compute allocation |
redis.external.enabled |
false |
Use externally managed Redis |
redis.external.host |
"" |
External Redis hostname |
redis.external.port |
6379 |
External Redis port |
redis.external.database |
0 |
Redis logical database |
redis.external.existingSecret |
"" |
Secret containing the Redis password |
redis.external.passwordKey |
redis-password |
Redis password key |
redis.external.networkPolicyPeers |
[] |
Allowed private Redis peers |
The dependency objects accept the additional values documented by the maintained HelmForge PostgreSQL and Redis charts. The defaults above are the values this parent chart sets explicitly.
Secrets, service and exposure
| Value | Default | Purpose |
|---|---|---|
externalSecrets.enabled |
false |
Render native ExternalSecret resources |
externalSecrets.refreshInterval |
1h |
Default synchronization interval |
externalSecrets.items |
[] |
Complete ExternalSecret definitions |
serviceAccount.create |
true |
Create a dedicated ServiceAccount |
serviceAccount.name |
"" |
Existing or explicit ServiceAccount name |
serviceAccount.annotations |
{} |
ServiceAccount annotations |
serviceAccount.automountServiceAccountToken |
false |
Keep the Kubernetes API token unmounted |
service.type |
ClusterIP |
Kubernetes Service type |
service.port |
3333 |
Application Service port |
service.annotations |
{} |
Service annotations |
service.ipFamilyPolicy |
"" |
Cluster default or explicit family policy |
service.ipFamilies |
[] |
Ordered Service address families |
ingress.enabled |
false |
Render an Ingress |
ingress.ingressClassName |
"" |
Explicit ingress class |
ingress.annotations |
{} |
Ingress annotations |
ingress.hosts |
[] |
Ingress hosts and paths |
ingress.tls |
[] |
Ingress TLS definitions |
gatewayAPI.enabled |
false |
Render HTTPRoutes |
gatewayAPI.rootUrlScheme |
https |
Scheme for an HTTPRoute-derived public URL |
gatewayAPI.httpRoutes |
[] |
HTTPRoute definitions and parents |
Health, security and scheduling
| Value | Default | Purpose |
|---|---|---|
probes.startup.enabled |
true |
Probe dependency-free startup health |
probes.startup.initialDelaySeconds |
5 |
Initial startup delay |
probes.startup.periodSeconds |
5 |
Startup probe period |
probes.startup.timeoutSeconds |
5 |
Startup probe timeout |
probes.startup.failureThreshold |
60 |
Migration and seed startup budget |
probes.liveness.enabled |
true |
Probe dependency-free process health |
probes.liveness.initialDelaySeconds |
0 |
Initial liveness delay |
probes.liveness.periodSeconds |
20 |
Liveness probe period |
probes.liveness.timeoutSeconds |
5 |
Liveness probe timeout |
probes.liveness.failureThreshold |
3 |
Failures before restart |
probes.readiness.enabled |
true |
Probe PostgreSQL and Redis readiness |
probes.readiness.initialDelaySeconds |
0 |
Initial readiness delay |
probes.readiness.periodSeconds |
10 |
Readiness probe period |
probes.readiness.timeoutSeconds |
5 |
Dependency health timeout |
probes.readiness.failureThreshold |
3 |
Failures before traffic withdrawal |
waitForServices.image.repository |
docker.io/library/busybox |
Official dependency-wait image |
waitForServices.image.tag |
1.37.0@sha256:... |
Immutable multi-architecture wait image |
waitForServices.image.pullPolicy |
IfNotPresent |
Wait-image pull policy |
waitForServices.timeoutSeconds |
180 |
Per-dependency TCP wait budget |
waitForServices.resources |
requests and limits | Wait-container compute allocation |
resources |
requests and limits | Ghostfolio compute allocation |
podSecurityContext |
non-root UID/GID 1000 | Pod identity, filesystem group and seccomp |
securityContext |
hardened | Read-only root, no escalation and no capabilities |
runtimeStorage.sizeLimit |
256Mi |
Size limit for /tmp and npm cache emptyDirs |
terminationGracePeriodSeconds |
60 |
Graceful shutdown budget |
networkPolicy.enabled |
true |
Apply ingress and egress isolation |
networkPolicy.egressIsolation |
true |
Enforce egress rules |
networkPolicy.ingressFrom |
[] |
Allowed ingress peers; empty means same namespace |
networkPolicy.dnsEgress |
[] |
DNS peers; empty allows port 53 generally |
networkPolicy.allowPublicInternet |
true |
Permit public HTTP/HTTPS market-data access |
networkPolicy.extraEgress |
[] |
Additional native egress rules |
podLabels |
{} |
Additional pod labels |
podAnnotations |
{} |
Additional pod annotations |
nodeSelector |
{} |
Required node labels |
tolerations |
[] |
Pod tolerations |
affinity |
{} |
Pod affinity rules |
topologySpreadConstraints |
[] |
Topology spreading rules |
priorityClassName |
"" |
Optional PriorityClass |
extraVolumes |
[] |
Additional pod volumes |
extraVolumeMounts |
[] |
Additional Ghostfolio volume mounts |
Every value is type-checked by the chart’s values.schema.json.
Uninstall
helm uninstall ghostfolio --namespace ghostfolio
Confirm the retention and backup state of PostgreSQL and Redis PVCs before uninstalling. Kubernetes and the storage provisioner control PVC lifecycle after removal.