Self-hosting
Deployment overview
Run Golden Reports on your own infrastructure - what to deploy, what to operate, and what to expect.
Golden Reports is a Kubernetes-native application. The official deployment artifact is the golden-reports Helm chart, which renders every workload, its dependencies, and the routing in front of them. This page is the high-level "what you're deploying" view. The chart's reference is on Helm chart reference.
What you'll deploy
A standard deployment consists of:
Application workloads
| Workload | Replicas (default) | Purpose |
|---|---|---|
| api | 2 | Platform API - control plane |
| query-engine | 2 | Query execution service |
| data-provider | 2 | Connector host |
| manager | 2 | Web UI |
Each workload runs its own container image; the chart wires them up. Image references resolve through global.imageRegistry so air-gapped deployments can mirror them into a private registry.
Backend components (api, query-engine, data-provider, plus the migrations Job) share a single backendVersion. The manager (the web UI) versions independently as frontendVersion. This separation lets you ship UI updates without redeploying the API and vice versa.
Operational dependencies
| Dependency | Bundled by chart? | Notes |
|---|---|---|
| PostgreSQL | Yes (via the CloudNativePG operator) | Or point to an external instance with postgresql.enabled: false + externalDatabase. |
| Redis | Yes | Or use externalRedis. |
| Object storage | No | Used for assets and exports. S3-compatible (AWS S3, MinIO, GCS via S3 API). |
| Identity provider | External, always | Email sign-up and OAuth (Google, Microsoft, etc.). Configured per deployment. |
The chart's bundled Postgres mode uses a CloudNativePG cluster - you'll need the CNPG operator pre-installed in the cluster.
Network routing
The chart can render either a classic Ingress or a Gateway API HTTPRoute per service (api and manager). Both gate independently; enabling both is almost certainly wrong because you'd get two parallel routes to the same Service. Pick the one your cluster supports.
Optional cert-manager integration handles TLS certs (Let's Encrypt or your internal CA) - see the certManager block on each ingress or httpRoute.
One-shot Jobs
- Schema migrations Job runs database migrations before the API starts. The chart wires it as a Helm pre-install/pre-upgrade hook so it always runs before the API rolls.
Prerequisites
Before installing:
- Kubernetes 1.27+ with a CNI that supports
NetworkPolicyif you intend to enable network isolation (Calico, Cilium, Weave). - Helm 3.13+.
- CloudNativePG operator in the cluster (only when bundled Postgres is enabled - the default).
- An Ingress controller or a Gateway API controller plus CRDs (Istio, Envoy Gateway, etc.).
- A container registry that can pull the platform's images. Air-gapped environments can mirror them into a private registry and set
global.imageRegistry. - An identity provider configured for the deployment (email sign-up plus OAuth providers - Google, Microsoft, etc.).
- A persistent volume class for the Postgres and Redis PVCs (or override
global.storageClass).
A first install
The chart's name is golden-reports. Install from a release artifact:
helm upgrade --install golden-reports oci://registry.wolfware.dev/charts/golden-reports \
--namespace golden-reports --create-namespace \
--set frontendVersion=<frontend-version> \
-f values.yaml
frontendVersion is a required override - it has no sensible default because frontend releases are decoupled from the chart version. backendVersion falls back to Chart.AppVersion when blank.
A minimal values.yaml for a development cluster:
backendVersion: "" # uses chart's appVersion
frontendVersion: "<frontend-version>"
components:
api:
ingress:
enabled: true
hosts:
- host: api.golden-reports.local
paths:
- path: /
pathType: Prefix
manager:
apiUrl: "" # relative routing
ingress:
enabled: true
hosts:
- host: app.golden-reports.local
paths:
- path: /
pathType: Prefix
postgresql:
enabled: true
cnpg:
instances: 1
storage:
size: 20Gi
redis:
enabled: true
For production you'll want at minimum: TLS via cert-manager, the bundled Postgres scaled to 3 instances, and an explicit existingSecret for Redis auth.
Sizing
A defensible default for a single small-tenant deployment:
| Component | CPU req | Memory req | Replicas |
|---|---|---|---|
| api | 250m | 512Mi | 2 |
| query-engine | 250m | 256Mi | 2 |
| data-provider | 250m | 256Mi | 2 |
| manager | 50m | 64Mi | 2 |
| postgresql (CNPG) | 500m | 1Gi | 1 (dev) / 3 (prod) |
| redis | 100m | 128Mi | 1 |
Real load varies wildly by report volume; enable HPAs (autoscaling.enabled: true per component) once you have traffic to measure. The query-engine is the most likely component to bottleneck - large fetches consume CPU during post-processing - so it's the first place to scale.
What you operate
Day-2 responsibilities:
- Backups. Postgres backups via CNPG (S3 or another supported backend). Asset object storage backups via your bucket's native tools.
- Upgrades. Backend and frontend version trains move independently. The chart's migrations Job runs on every
helm upgrade; failures block the API rollout. - Secrets. Database password, Redis password, identity-provider configuration, image pull secrets. The chart accepts each as an
existingSecretreference; let your secret manager (SealedSecrets, External Secrets, Vault) own the source of truth. - TLS. cert-manager integration is built-in. Without it, supply
tls:blocks directly. - Observability. All workloads expose Prometheus metrics on the standard port and OpenTelemetry trace context propagates through the request path. See Observability.
- Plugin management. Connector and block plugins are managed through the in-product Marketplace; ops involvement is limited to making sure outbound network to the Marketplace CDN is allowed.
What you do not operate
A short list to set expectations:
- Authentication. The identity provider handles users, sessions, and OAuth.
- CDN for plugin distribution. Marketplace plugins are served from the platform's CDN.
- Editor bundle hosting. The Manager image already includes the report editor.
- Per-tenant data isolation. Multi-tenancy is structural - you don't run one cluster per tenant.
Air-gapped and private deployments
A few flags accommodate restricted environments:
global.imageRegistry: "registry.example.com"- mirrors all platform images through your registry.global.imagePullSecrets: [{ name: regcred }]- applies to every Pod.externalDatabase.*/externalRedis.*- point to your managed Postgres/Redis instead of the bundled ones.networkPolicy.enabled: true- generate restrictive NetworkPolicies per workload.- Side-loaded connectors - mount a directory of
.wasmblobs into the data provider Pod viaenv/envFromandvolumeMounts(extra Pod-level overrides go through componentenvand external secret refs).
Marketplace access can be turned off entirely for fully air-gapped environments - plugins are then installed by uploading bundles to the workspace's plugin registry directly. Hardening profiles for restricted environments are available on request through your account team.
What's not (yet) supported
For honesty:
- Cross-region active-active. A single Kubernetes deployment is a single region. Multi-region with shared state isn't supported today; multi-region with per-tenant pinning is achievable by running multiple deployments behind a routing layer.
- Per-tenant resource isolation. The chart deploys a single set of workloads serving all tenants. Customers needing physical isolation per tenant run separate Helm releases.
Next
- Configure every knob the chart exposes: Helm chart reference.
- Wire up metrics, logs, and traces: Observability.
- Understand what each component does: System overview.