Skip to content

Helm ​

deploy/helm/report-forge runs Reportforge on Kubernetes: one reportd as a StatefulSet with a volume for /data, and renderd inside it or as its own Deployment. It runs the image docker.md describes, configured through REPORTD_ variables.

bash
helm install reports deploy/helm/report-forge -n reports --create-namespace \
  --set image.repository=registry.example.com/report-forge --set image.tag=0.9.0
kubectl logs -n reports statefulset/reports-report-forge | grep setupCode

The first start is setup mode (setup.md): NOTES.txt prints how to get the setup code and reach the studio. admin.existingSecret skips it.

What it runs ​

  • reportd: a StatefulSet with one replica and a PersistentVolumeClaim for /data (persistence.size, persistence.storageClass), behind a Service on 43123. replicaCount above 1 fails the install: the template store can't be shared yet. For more renders, use renderer.mode: separate.
  • Pod security: uid and gid 65532 with fsGroup 65532 (so the volume is writable), runAsNonRoot, a read-only root filesystem with an emptyDir for /tmp, every capability dropped, no privilege escalation, and the RuntimeDefault seccomp profile. renderd's pods run the same way.
  • Probes: liveness on /livez; readiness on /readyz, which checks the engine and, on Postgres, the database; a startup probe allowing 10 minutes (startupProbe) for a data migration.
  • Ingress (ingress.enabled): ingress.host, ingress.className, ingress.tlsSecretName and ingress.annotations. publicURL defaults to https://<ingress.host>.
  • helm test runs reportd healthcheck --ready against the Service.

Settings ​

ValuesBecome
configREPORTD_ variables, one per setting. Nested maps and dotted keys both make the path, so config: {logging: {format: json}} and config: {logging.format: json} are REPORTD_LOGGING_FORMAT; lists are comma-separated.
configFile/config/config.yaml, from a ConfigMap: for lists of objects, such as sign-in providers and connections, which variables can't hold. config wins over it.
secretsKeys of existing Secrets as files under /run/secrets/, for secret:file:<file> references: - {secretName: sso, key: client-secret, file: oidc-secret} and clientSecret: secret:file:oidc-secret in configFile.
publicURLREPORTD_PUBLICURL.
admin.existingSecret, admin.passwordKey, admin.usernameThe first administrator's password from a Secret (secret:file:admin-password): no setup mode.
state.postgres.existingSecret, urlKeystate.driver: postgres, with the connection URL from that Secret (state.md). No Postgres is bundled: use an operator or a managed service.
state.postgres.existingKeysSecretOn Postgres every node must share auth.mfaKey and viewer.linkSecret: a Secret with mfa-key and link-key, or one the chart makes once and keeps (helm.sh/resource-policy: keep, since losing mfa-key loses every TOTP secret).

A setting the chart sets (the public URL, Postgres, the renderer) is shown as fixed in the setup wizard, with the variable it comes from.

The licence (licence.md) comes from a Secret through secrets and config:

yaml
secrets:
  - {secretName: report-forge-licence, key: licence, file: licence}
config:
  licence: secret:file:licence

state.postgres.existingSecret and renderer.mode: separate are "scale" features (licence.md). A new install with them starts, but the setup wizard won't finish without a licence with scale. Setting the licence from a Secret, as above, avoids that step. Without them, the chart's defaults (one reportd, renderd inside it, no Postgres) run as Community.

renderd on its own ​

yaml
renderer:
  mode: separate        # inprocess (the default) or separate
  replicas: 3
  resources:
    requests: {cpu: "1", memory: 512Mi}
    limits: {cpu: "2", memory: 2Gi}
  autoscaling:
    enabled: true       # a HorizontalPodAutoscaler on CPU; off by default
    minReplicas: 2
    maxReplicas: 8
    targetCPUUtilizationPercentage: 70
  • separate adds a renderd Deployment behind a headless Service. reportd looks its name up every 5 seconds and spreads renders over every ready pod (docker.md), so new pods get work as soon as they're ready.
  • Each pod's worker count follows its CPU limit. renderd's /health reports its workers, renders running and queue, for a later autoscaler on the queue.
  • The token renderd requires comes from a Secret the chart makes once and keeps, or renderer.existingTokenSecret (key renderer.tokenKey).
  • A NetworkPolicy lets only reportd's pods reach renderd (renderer.networkPolicy.enabled); it needs a network plugin that enforces policies.
  • Liveness and readiness run renderd --healthcheck, which counts a busy renderd as up, so a busy pod isn't restarted or taken out of the Service.

Checks ​

deploy/helm/check.sh lints the chart and renders it with each values file in deploy/helm/ci/ (in-process, separate, separate with autoscaling, Postgres with SSO, Ingress), validates every manifest with kubeconform, and checks that two replicas are refused. CI runs it.