Skip to content

docs: add Helm install path to enterprise Analytics guide#524

Open
jpshackelford wants to merge 2 commits into
mainfrom
jpshackelford/analytics-helm-tab
Open

docs: add Helm install path to enterprise Analytics guide#524
jpshackelford wants to merge 2 commits into
mainfrom
jpshackelford/analytics-helm-tab

Conversation

@jpshackelford
Copy link
Copy Markdown
Contributor

@jpshackelford jpshackelford commented May 26, 2026

Problem Statement

enterprise/analytics is 100% Replicated/KOTS — every step ("Enable Analytics" checkbox, "Save config", "Deploy") is a click in the Admin Console. Customers doing a Helm install into their own Kubernetes cluster (e.g. C3.ai on GKE — see slack thread) have no documented path; they edit a `site-values.yaml` and run `helm upgrade`.

With more Helm-based installs coming, the analytics doc needs proper coverage for both paths.

Proposed Solution

Single page, single URL, with `` only on the install-specific steps. The Laminar-side workflow (Keycloak login → create project → create ingest-only key → view traces) is identical across paths and stays outside the tabs.

Three places get a tab block:

  • Enable Analytics — KOTS checkbox vs `site-values.yaml` `laminar:` block + `env.LMNR_*` keys
  • Deploy — Admin Console redeploy vs `helm upgrade` + `kubectl rollout`
  • Wire the API key into the install — KOTS field + Save config vs `env.LMNR_PROJECT_API_KEY` + `helm upgrade`

Also adds a cross-link card from `enterprise/k8s-install/index.mdx`, and an `analytics.app.` row to the Quick Start "Reasons for Requirements" table so the SAN is included at install time (complements #523).

See follow-up comment for content provenance, screenshot handling, and out-of-scope notes.

Checklist

  • I have read and reviewed the documentation changes to the best of my ability.
  • If the change is significant, I have run the documentation site locally and confirmed it renders as expected.

This PR was created by an AI agent (OpenHands) on John-Mason's behalf.

Restructures enterprise/analytics.mdx into a single page with tabbed
'Replicated (VM Install)' vs 'Helm (Kubernetes Install)' sections for
the two steps where the install paths actually diverge:

- 'Enable Analytics' (KOTS checkbox vs site-values.yaml laminar: block
  plus env.LMNR_BASE_URL/LMNR_FORCE_HTTP keys, with options for
  pre-existing TLS secrets and cert-manager)
- 'Deploy' (Admin Console redeploy vs helm upgrade + kubectl rollout)
- 'Wire the API key into the install' (KOTS field + Save config vs
  env.LMNR_PROJECT_API_KEY + helm upgrade)

The Laminar-side workflow (Keycloak login, create project, create
ingest-only key, view traces) is shared between paths and stays
outside the tabs.

Helm-tab content is distilled from the OpenHands-Cloud chart values,
the All-Hands-AI/deploy production reference, and the upstream
lmnr-helm CONFIGURATION.md, with a Tip pointing AWS users at
appServer.loadBalancer.

Also:
- enterprise/k8s-install/index.mdx: add Analytics card to Guides
- enterprise/quick-start.mdx: add analytics.app.<domain> row to the
  'Reasons for Requirements' table so the SAN is included at install
  time and customers don't have to re-issue certs later (complements
  PR #523 which added the row to the DNS table and preflight script)

Co-authored-by: openhands <openhands@all-hands.dev>
Copy link
Copy Markdown
Contributor Author

Background detail (moved from PR description)

Helm tab content — provenance

The laminar: YAML block and the env.LMNR_* keys are distilled from:

Includes a <Tip> pointing AWS users at laminar.appServer.loadBalancer.enabled: true (NLB) as an alternative to the Ingress block.

Screenshots

All existing screenshots are reused as-is. The Replicated-only screenshots (laminar-configure-analytics.png, laminar-deploy-in-progress.png, laminar-deployment-status-details.png, laminar-configure-key.png, laminar-deploy-again.png) move into the Replicated (VM Install) tab. The Helm tab is YAML + bash blocks; no new screenshots are needed.

What this PR does not do

  • It does not add a Helm install tab to enterprise/quick-start.mdx itself. Quick Start's tabs are still scoped to infrastructure provisioning (AWS Terraform vs Manual VM), both of which converge on the same Replicated install. A separate effort is needed to flesh out the Helm install Quick Start; this PR only addresses analytics.
  • It does not regenerate llms.txt/llms-full.txt. Those are picked up by the weekly sync-llms-files workflow.

IA precedent

This is a small IA precedent that doesn't match the existing quick-startk8s-install/ split — Helm-specific content otherwise lives under the K8s Install group. The decision to use tabs on the analytics page rather than a separate k8s-install/analytics.mdx was a deliberate call (analytics being the "tip of the spear" for Helm coverage), so the precedent will likely extend to other pages over time.

@mintlify
Copy link
Copy Markdown

mintlify Bot commented May 26, 2026

Preview deployment for your docs. Learn more about Mintlify Previews.

Project Status Preview Updated (UTC)
all-hands-ai 🟢 Ready View Preview May 26, 2026, 3:42 PM

💡 Tip: Enable Workflows to automatically generate PRs for you.

@jpshackelford jpshackelford marked this pull request as ready for review May 26, 2026 16:18
Copy link
Copy Markdown
Contributor

@all-hands-bot all-hands-bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Well-structured documentation that successfully adds Helm install coverage for analytics. The tabs pattern works well, and the YAML configuration is comprehensive. Two suggestions for security best practices and clarity.


Was this automated review useful? React with 👍 or 👎 to this review to help us measure review quality.
Workflow run: https://github.com/OpenHands/docs/actions/runs/26460674595

Comment thread enterprise/analytics.mdx
env:
LMNR_BASE_URL: "https://laminar-api.app.example.com"
LMNR_FORCE_HTTP: "true"
LMNR_PROJECT_API_KEY: "<paste-your-ingest-only-key-here>"
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Suggestion: The main example shows the API key in plaintext YAML, which could lead users to commit secrets to version control. Consider showing the Secret-based approach as the primary example:

env:
  LMNR_BASE_URL: "https://laminar-api.app.example.com"
  LMNR_FORCE_HTTP: "true"
  LMNR_PROJECT_API_KEY:
    valueFrom:
      secretKeyRef:
        name: laminar-api-key
        key: api-key

Then show how to create the secret:

kubectl create secret generic laminar-api-key \
  -n openhands --from-literal=api-key='<your-ingest-only-key>'

Comment thread enterprise/analytics.mdx
Comment on lines +109 to +120
- name: AUTH_KEYCLOAK_ID
valueFrom:
secretKeyRef:
name: keycloak-realm
key: client-id
- name: AUTH_KEYCLOAK_SECRET
valueFrom:
secretKeyRef:
name: keycloak-realm
key: client-secret
- name: AUTH_KEYCLOAK_ISSUER
value: "https://auth.app.example.com/realms/allhands"
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Suggestion: The config references a keycloak-realm secret that's created by the Replicated installer but may not exist in Helm installs. Add a note explaining where this comes from:

<Note>
  The `keycloak-realm` secret is created automatically by the OpenHands Helm chart during initial install. If you need to update Keycloak configuration, see the [chart documentation](https://github.com/OpenHands/OpenHands-Cloud/blob/main/charts/openhands/README.md).
</Note>

Copy link
Copy Markdown
Contributor

@all-hands-bot all-hands-bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Solid, well-structured addition. The tabbed layout cleanly separates the two install paths without duplicating the non-path-specific steps, the YAML examples are thorough and well-commented, and the cross-linking to prerequisites is accurate. Three inline suggestions below — none are blockers.


This review was generated by an AI agent (OpenHands) on behalf of the user through OpenHands Automation.

Comment thread enterprise/analytics.mdx
enabled: true
global:
# Sets provider-specific defaults; not auto-detected. Use "gcp" or "aws".
cloudProvider: "gcp"
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Suggestion: cloudProvider: "gcp" is a concrete value in a copy-paste snippet, but the comment only lists "gcp" or "aws" as valid options. Users on Azure or bare-metal may set this to an unsupported value silently and get unexpected defaults from the upstream lmnr-helm chart.

Consider either expanding the comment to list all valid values (or link directly to the relevant section of the lmnr-helm config guide), or use a placeholder like <gcp|aws> to make it clear this must be substituted:

Suggested change
cloudProvider: "gcp"
# Sets provider-specific defaults; not auto-detected. Valid values: "gcp", "aws".
# See https://github.com/lmnr-ai/lmnr-helm/blob/main/CONFIGURATION.md for other environments.
cloudProvider: "gcp"

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Some customers will be in AWS? We may want to specify to the user with a comment specify your cloud provider AWS or GCP.

The cloud provider also impacts laminar storage. Customers might be using a different storage type than the default. Defaults are listed here in the laminar chart values https://github.com/lmnr-ai/lmnr-helm/blob/main/charts/laminar/values.yaml#L837. The GCP default pd-balanced was not available for us in the SaaS GKE cluster. For SaaS in GCP GKE, there's hyperdisk-balanced https://github.com/OpenHands/deploy/blob/main/openhands/envs/production/values.yaml#L183. I checked in the cluster and that was the storage class type available.

Comment thread enterprise/analytics.mdx
name: keycloak-realm
key: client-secret
- name: AUTH_KEYCLOAK_ISSUER
value: "https://auth.app.example.com/realms/allhands"
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Suggestion: The instructions above say "Replace example.com with your base domain" but don't address the allhands realm name in the issuer URL. If allhands is always the fixed Keycloak realm in OpenHands Enterprise, that should be stated explicitly so users don't wonder whether it's a placeholder. If it can vary, it should be listed as a substitution alongside example.com.

A brief inline comment would resolve the ambiguity:

Suggested change
value: "https://auth.app.example.com/realms/allhands"
- name: AUTH_KEYCLOAK_ISSUER
value: "https://auth.app.example.com/realms/allhands" # realm name is fixed as "allhands" in OpenHands Enterprise

Comment thread enterprise/analytics.mdx
helm upgrade --install openhands \
--namespace openhands \
oci://ghcr.io/all-hands-ai/helm-charts/openhands \
-f site-values.yaml
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Suggestion: The helm upgrade --install command here (and again at line 283) doesn't pin a chart version. Without --version, running this command at a later date will silently upgrade to whatever the latest OCI tag is at that time, which could introduce breaking changes in a production cluster.

Consider adding a note that users should pin the version to match what they originally installed, e.g.:

Suggested change
-f site-values.yaml
oci://ghcr.io/all-hands-ai/helm-charts/openhands \
--version <chart-version> \ # use the same version as your existing install; omit to upgrade to latest
-f site-values.yaml

Alternatively, a brief prose note like "If you want to stay on your current chart version, add --version <X.Y.Z>." would be sufficient.

The previous draft mirrored the All-Hands SaaS production values, which
expose an external Laminar app-server ingress at laminar-api.app... .
That's necessary for SaaS because the runtime fleet can span clusters,
but it's over-specified for typical single-cluster Helm customers, who
have no current multi-cluster installs.

Update the Helm tab so the primary path uses the in-cluster Laminar
Service for trace ingestion -- LMNR_BASE_URL=http://laminar-app-server-service,
LMNR_FORCE_HTTP=true, LMNR_HTTP_PORT=8000 -- matching what the Replicated
install does. This means customers only need ONE new DNS record and TLS
SAN (the user-facing analytics.app.<base-domain> hostname) instead of two.

Move the laminar.appServer.ingress / loadBalancer configuration into an
<Accordion> labelled 'Advanced: expose the app-server externally
(multi-cluster only)' so it's discoverable but doesn't pollute the
default path.

Also update the 'Wire the API key' Helm code block to reflect the new
LMNR_BASE_URL value.

Co-authored-by: openhands <openhands@all-hands.dev>
Copy link
Copy Markdown
Contributor Author

Revision: in-cluster ingest is now the default Helm path

JM pushed back on the previous draft: since we have zero multi-cluster customers, two new DNS records and two TLS SANs is over-specified.

The Helm tab now mirrors what Replicated actually does — `LMNR_BASE_URL: "http://laminar-app-server-service\"\`, `LMNR_FORCE_HTTP: "true"`, `LMNR_HTTP_PORT: "8000"`, no `laminar.appServer` block. One new DNS record + TLS SAN for the user-facing Laminar UI (`analytics.app.`), matching the Quick Start row added in #523.

The external app-server ingress / NLB pattern is preserved inside an `` labeled "Advanced: expose the app-server externally (multi-cluster only)" so it's discoverable but doesn't pollute the default path.

Pushed in 05e582b.

Comment thread enterprise/analytics.mdx
env:
# The application sends traces to the in-cluster Laminar Service. No external
# hostname is required for ingestion.
LMNR_BASE_URL: "http://laminar-app-server-service"
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If it is a one cluster setup then I think this is fine

Comment thread enterprise/analytics.mdx
tls:
enabled: true
clusterIssuer: "" # see TLS options below
secretName: "laminar-frontend-tls"
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The customer can reuse the openhands cert secret here instead

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants