Skip to main content

Helm base chart

The base chart deploys the application itself: Deployment, Service, ServiceAccount, HorizontalPodAutoscaler, and a Gateway API HTTPRoute. It does not provision databases, secrets, or observability — see the platform chart for those.

Values reference: helm/base/values.yaml

Install

helm install ghostfolio ./helm/base \
--set gateway.hostnames[0]=ghostfolio.yourdomain.com

Configuration

Environment variables

Set env to inject environment variables into the container:

env:
- name: LOG_LEVEL
value: "info"
# - name: DATABASE_URL # usually injected via envFrom from a Secret
# value: "..."

Injecting secrets

Don't put credentials in env. Use envFrom to pull from a Secret — typically one generated by the platform chart's ExternalSecrets integration:

envFrom:
- secretRef:
name: ghostfolio

See the platform chart docs for how the Secret gets populated.

Ingress

The base chart emits a Gateway API HTTPRoute. A parent Gateway (with listeners and TLS) is expected to be provided by the platform — typically a gateway chart such as hiroba-gateway — so the app chart only owns the route itself.

Minimum configuration

gateway:
parentRefs:
- name: default-gateway
namespace: gateway-system
sectionName: https # pin to the HTTPS listener
hostnames:
- ghostfolio.yourdomain.com

Pin sectionName to an HTTPS listener to avoid silently serving plaintext. HTTP→HTTPS redirect is a listener-level concern and lives on the parent Gateway, not in this chart.

Custom routing rules

The default catch-all sends all traffic to the service. To split paths (e.g. /api to a different backend) set gateway.rules:

gateway:
rules:
- matches:
- path:
type: PathPrefix
value: /api
filters:
- type: RequestHeaderModifier
requestHeaderModifier:
add:
- name: X-Forwarded-Prefix
value: /api

Scaling

Horizontal autoscaling is off by default:

autoscaling:
enabled: true
minReplicas: 2
maxReplicas: 10
targetCPUUtilizationPercentage: 80

PodDisruptionBudget

Once you're running more than one replica, enable a PDB so node drains / cluster upgrades can't take the app fully offline:

podDisruptionBudget:
enabled: true
minAvailable: 1
# maxUnavailable: "25%" # mutually exclusive — set minAvailable: null to use this

The selector reuses app.selectorLabels, so it matches the Deployment's pods automatically — no cross-release label coupling needed.

Probes

Liveness and readiness probes target /healthz and /readyz on the container port. Override the defaults in livenessProbe / readinessProbe if your application exposes different paths.

Security context

The pod runs as UID 1000 with readOnlyRootFilesystem: true and all Linux capabilities dropped. If your application needs to write to the filesystem, add an emptyDir volume under extraVolumes / extraVolumeMounts rather than loosening the security context.