Skip to content

Nebari integration

With nebariapp.enabled: true (the default), the chart renders a NebariApp resource. The nebari-operator turns it into an HTTPRoute, a TLS certificate, a Keycloak client, and — optionally — a tile on the Nebari landing page.

nebariapp:
enabled: true
hostname: grafana.example.com
keycloakHostname: keycloak.example.com

The namespace must also be opted into Nebari management, or the operator ignores the resource:

Terminal window
kubectl label namespace monitoring nebari.dev/managed=true

Under Argo CD, syncPolicy.managedNamespaceMetadata applies that label for you — see Deploying on Nebari.

By default it targets the Grafana service created by the subchart:

ValueDefaultPurpose
nebariapp.service.name<release>-grafanaBackend service.
nebariapp.service.port80Service port (Grafana’s container port is 3000).
nebariapp.routing.routes[{ pathPrefix: / }]Grafana owns the whole host.

When nebariapp.auth.enabled is true and keycloakHostname is set, the chart renders a grafana-oauth-config ConfigMap carrying every GF_AUTH_GENERIC_OAUTH_* variable, and Grafana consumes it via envFromConfigMaps (marked optional: true, so a release without Keycloak just falls back to local accounts). The client secret is read from the <fullname>-oidc-client Secret that the operator creates when it provisions the client.

ValueDefaultPurpose
nebariapp.auth.enabledtrueProvision a Keycloak client and turn on Grafana OAuth.
nebariapp.auth.providerkeycloakIdentity provider.
nebariapp.auth.provisionClienttrueLet the operator create the client. Set false and supply auth.clientSecretRef to bring your own.
nebariapp.auth.enforceAtGatewayfalseKeep gateway enforcement off — see below.
nebariapp.auth.redirectURI/login/generic_oauthGrafana’s OAuth callback path.
nebariapp.auth.scopesopenid, profile, email, groupsRequested scopes.
nebariapp.auth.groupsadmin, viewerGroups created in the realm.
nebariapp.keycloakHostnameKeycloak host. Required when auth is on.
nebariapp.keycloakRealmnebariRealm name.
nebariapp.keycloakBasePath""Set to /auth for legacy Keycloak (< v17).

enforceAtGateway: false is deliberate. Grafana runs its own OIDC flow, so an Envoy Gateway SecurityPolicy in front of it would authenticate the user a second time and interfere with the /login/generic_oauth callback. Auth is enforced inside Grafana, which is also what makes role mapping possible.

Grafana derives its role from the groups claim with a single JMESPath expression:

contains(groups[*], 'admin') && 'Admin' || 'Viewer'

Members of admin become Grafana Admins; everyone else is a Viewer. AUTO_LOGIN is on, so users land on Keycloak rather than Grafana’s own login form, and ALLOW_SIGN_UP is on, so first-time realm users get a Grafana account created for them.

nebariapp.auth.keycloakConfig.groups seeds membership. The default puts the admin user in the admin group:

nebariapp:
auth:
keycloakConfig:
groups:
- name: admin
members: [admin]
- name: viewer
members: [alice, bob]

Groups named in auth.groups but absent from keycloakConfig.groups are appended automatically with no members. A group-membership protocol mapper (oidc-group-membership-mapper, claim groups, full.path: false) is injected automatically whenever groups are configured and you have not supplied your own keycloakConfig.protocolMappers — without it the groups claim never reaches Grafana and everyone is a Viewer.

Off by default. Turn it on to give Grafana a card on the Nebari landing page:

nebariapp:
landingPage:
enabled: true
ValueDefault
displayNameGrafana
descriptionMetrics, logs, and traces observability platform
iconhttps://grafana.com/static/img/menu/grafana2.svg
categoryObservability
priority10 (lower sorts first, 0–1000)
externalUrlunset — derived from hostname
healthCheck.enabledtrue
healthCheck.path/api/health
healthCheck.intervalSeconds30 (10–300)
healthCheck.timeoutSeconds5 (1–30)

This requires a nebari-operator build with LandingPageConfig support.

By default only Grafana is routed. To make Loki, Tempo, or Mimir reachable from outside the cluster — for an external agent shipping telemetry in — add them as additionalServices:

nebariapp:
additionalServices:
- name: loki-push
service: { name: lgtm-pack-loki, port: 3100 }
routing:
routes:
- pathPrefix: /loki
pathType: Prefix
- name: tempo-push
service: { name: lgtm-pack-tempo, port: 4318 }
routing:
routes:
- pathPrefix: /tempo
pathType: Prefix
- name: mimir-push
service: { name: lgtm-pack-mimir, port: 8080 } # distributed: -mimir-gateway, port 80
routing:
routes:
- pathPrefix: /mimir
pathType: Prefix
Terminal window
kubectl -n monitoring get nebariapp
kubectl -n monitoring describe nebariapp lgtm-pack-nebari-lgtm-pack
# The operator writes the client secret here once the Keycloak client exists
kubectl -n monitoring get secret lgtm-pack-nebari-lgtm-pack-oidc-client
# What Grafana actually received
kubectl -n monitoring get cm grafana-oauth-config -o yaml

If Grafana shows its local login form instead of redirecting to Keycloak, the usual cause is a missing keycloakHostname — the OAuth ConfigMap is not rendered at all in that case, and the optional: true mount means Grafana starts anyway with no indication of what is wrong.

Terminal window
helm install lgtm-pack nebari/nebari-lgtm-pack --set nebariapp.enabled=false

No NebariApp, no OAuth ConfigMap. Reach Grafana by port-forward or your own Ingress, and sign in with grafana.adminUser / grafana.adminPassword.