Appearance
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 setupCodeThe 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.replicaCountabove 1 fails the install: the template store can't be shared yet. For more renders, userenderer.mode: separate. - Pod security: uid and gid 65532 with
fsGroup65532 (so the volume is writable),runAsNonRoot, a read-only root filesystem with anemptyDirfor/tmp, every capability dropped, no privilege escalation, and theRuntimeDefaultseccomp 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.tlsSecretNameandingress.annotations.publicURLdefaults tohttps://<ingress.host>. helm testrunsreportd healthcheck --readyagainst the Service.
Settings
| Values | Become |
|---|---|
config | REPORTD_ 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. |
secrets | Keys 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. |
publicURL | REPORTD_PUBLICURL. |
admin.existingSecret, admin.passwordKey, admin.username | The first administrator's password from a Secret (secret:file:admin-password): no setup mode. |
state.postgres.existingSecret, urlKey | state.driver: postgres, with the connection URL from that Secret (state.md). No Postgres is bundled: use an operator or a managed service. |
state.postgres.existingKeysSecret | On 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:licencestate.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: 70separateadds 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
/healthreports 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(keyrenderer.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.