Skip to content

feat!: move the Loki UI to a Grafana Plugin - #19390

Merged
trevorwhitney merged 11 commits into
mainfrom
twhitney/operational-ui-hackathon
Oct 7, 2025
Merged

feat!: move the Loki UI to a Grafana Plugin#19390
trevorwhitney merged 11 commits into
mainfrom
twhitney/operational-ui-hackathon

Conversation

@trevorwhitney

Copy link
Copy Markdown
Collaborator

What this PR does / why we need it:

This PR removes the javascript and ckit dependencies from the Loki repo.

The javascript was there to serve a static Operational UI. This UI is not going away, and all the APIs that serve it will remain in Loki. The Javascript, however, had been moved to the Loki Operational UI Grafana plugin. So, to continue using the UI, please install this plugin in a Grafana that has access to the URLs you were previously using for the UI itself, as it will use all the same APIs.

The decision to move the Operational UI was done by consensus of the Loki engineers for a couple reasons:

  • not everyone contributing to the Loki project is comfortable with Javascript/Typescript, making troubleshooting failing CI issue difficult
  • reduce CVE/security surface area, particularly given the many recent supply chain attacks in the Javascript ecosystem. The plugin will benefit from CI shared by other Grafana plugins to mitigate some of these risks and handle updates

This PR also removes ckit, since it was not needed for APIs or proxy layer. This layer was converted to using a dskit ring, which is a more mature library that Loki is already dependent on, and Loki engineers are more familiar with.

Checklist

  • Reviewed the CONTRIBUTING.md guide (required)
  • Documentation added
  • Tests updated
  • Title matches the required conventional commits format, see here
    • Note that Promtail is considered to be feature complete, and future development for logs collection will be in Grafana Alloy. As such, feat PRs are unlikely to be accepted unless a case can be made for the feature actually being a bug fix to existing behavior.
  • Changes that require user attention or interaction to upgrade are documented in docs/sources/setup/upgrade/_index.md
  • If the change is deprecating or removing a configuration option, update the deprecated-config.yaml and deleted-config.yaml files respectively in the tools/deprecated-config-checker directory. Example PR
@trevorwhitney
trevorwhitney marked this pull request as ready for review October 3, 2025 22:43
@trevorwhitney
trevorwhitney requested a review from a team as a code owner October 3, 2025 22:43
@trevorwhitney
trevorwhitney force-pushed the twhitney/operational-ui-hackathon branch from f617a8b to c4cd923 Compare October 3, 2025 22:46
@github-actions

github-actions Bot commented Oct 3, 2025

Copy link
Copy Markdown
Contributor

💻 Deploy preview deleted.

@rfratto rfratto left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

LGTM, though we might want to flag this as a breaking change to the configuration since people seem to be relying on the UI?

@trevorwhitney trevorwhitney changed the title feat: move the Loki UI to a Grafana Plugin Oct 6, 2025
@github-actions

github-actions Bot commented Oct 7, 2025

Copy link
Copy Markdown
Contributor

Helm Diff Output - Summary

Default Values Scenario-diff-output
default, loki-read, Deployment (apps) has changed:
  # Source: loki/templates/read/deployment-read.yaml
  apiVersion: apps/v1
  kind: Deployment
  metadata:
    name: loki-read
    namespace: default
    labels:
      app.kubernetes.io/part-of: memberlist
      helm.sh/chart: loki-6.42.0
      app.kubernetes.io/name: loki
      app.kubernetes.io/instance: loki-release
      app.kubernetes.io/version: "3.5.5"
      app.kubernetes.io/component: read
  spec:
    replicas: 1
    strategy:
      rollingUpdate:
        maxSurge: 0
        maxUnavailable: 1
    revisionHistoryLimit: 10
    selector:
      matchLabels:
        app.kubernetes.io/name: loki
        app.kubernetes.io/instance: loki-release
        app.kubernetes.io/component: read
    template:
      metadata:
        annotations:
          checksum/config: afc4770faa248a744723fe521d644449dbc7de4a189a88072560a74feacd35fc
        labels:
          app.kubernetes.io/part-of: memberlist
          app.kubernetes.io/name: loki
          app.kubernetes.io/instance: loki-release
          app.kubernetes.io/component: read
      spec:
        serviceAccountName: loki
        automountServiceAccountToken: true
        
        securityContext:
          fsGroup: 10001
          runAsGroup: 10001
          runAsNonRoot: true
          runAsUser: 10001
        terminationGracePeriodSeconds: 30
        containers:
          - name: loki
            image: docker.io/grafana/loki:3.5.5
            imagePullPolicy: IfNotPresent
            args:
              - -config.file=/etc/loki/config/config.yaml
-             - -target=read
-             - -legacy-read-mode=false
+             - -target=%sread- -legacy-read-mode=false
              - -common.compactor-grpc-address=loki-backend.default.svc.cluster.local:9095
            ports:
              - name: http-metrics
                containerPort: 3100
                protocol: TCP
              - name: grpc
                containerPort: 9095
                protocol: TCP
              - name: http-memberlist
                containerPort: 7946
                protocol: TCP
            securityContext:
              allowPrivilegeEscalation: false
              capabilities:
                drop:
                - ALL
              readOnlyRootFilesystem: true
            readinessProbe:
              httpGet:
                path: /ready
                port: http-metrics
              initialDelaySeconds: 30
              timeoutSeconds: 1
            volumeMounts:
              - name: config
                mountPath: /etc/loki/config
              - name: runtime-config
                mountPath: /etc/loki/runtime-config
              - name: tmp
                mountPath: /tmp
              - name: data
                mountPath: /var/loki
            resources:
              {}
        affinity:
          podAntiAffinity:
            requiredDuringSchedulingIgnoredDuringExecution:
            - labelSelector:
                matchLabels:
                  app.kubernetes.io/component: read
                  app.kubernetes.io/instance: 'loki-release'
                  app.kubernetes.io/name: 'loki'
              topologyKey: kubernetes.io/hostname
        volumes:
          - name: tmp
            emptyDir: {}
          - name: data
            emptyDir: {}
          - name: config
            configMap:
              name: loki
              items:
                - key: "config.yaml"
                  path: "config.yaml"
          - name: runtime-config
            configMap:
              name: loki-runtime
default, loki-release-gateway, ConfigMap (v1) has changed:
  # Source: loki/templates/gateway/configmap-gateway.yaml
  apiVersion: v1
  kind: ConfigMap
  metadata:
    name: loki-release-gateway
    namespace: default
    labels:
      helm.sh/chart: loki-6.42.0
      app.kubernetes.io/name: loki
      app.kubernetes.io/instance: loki-release
      app.kubernetes.io/version: "3.5.5"
      app.kubernetes.io/component: gateway
  data:
    nginx.conf: |
      worker_processes  5;  ## Default: 1
      error_log  /dev/stderr;
      pid        /tmp/nginx.pid;
      worker_rlimit_nofile 8192;
      
      events {
        worker_connections  4096;  ## Default: 1024
      }
      
      http {
        client_body_temp_path /tmp/client_temp;
        proxy_temp_path       /tmp/proxy_temp_path;
        fastcgi_temp_path     /tmp/fastcgi_temp;
        uwsgi_temp_path       /tmp/uwsgi_temp;
        scgi_temp_path        /tmp/scgi_temp;
      
        client_max_body_size  4M;
      
        proxy_read_timeout    600; ## 10 minutes
        proxy_send_timeout    600;
        proxy_connect_timeout 600;
      
        proxy_http_version    1.1;
      
        default_type application/octet-stream;
        log_format   main '$remote_addr - $remote_user [$time_local]  $status '
              '"$request" $body_bytes_sent "$http_referer" '
              '"$http_user_agent" "$http_x_forwarded_for"';
        access_log   /dev/stderr  main;
      
        sendfile     on;
        tcp_nopush   on;
        resolver kube-dns.kube-system.svc.cluster.local.;
      
        # if the X-Query-Tags header is empty, set a noop= without a value as empty values are not logged
        map $http_x_query_tags $query_tags {
          ""        "noop=";            # When header is empty, set noop=
          default   $http_x_query_tags; # Otherwise, preserve the original value
        }
      
        server {
          listen             8080;
          listen             [::]:8080;
      
          location = / {
            
            return 200 'OK';
            auth_basic off;
          }
      
          ########################################################
          # Configure backend targets
          location ^~ /ui {
            
-           proxy_pass       http://loki-write.default.svc.cluster.local:3100$request_uri;
+           proxy_pass       http://loki-read.default.svc.cluster.local:3100$request_uri;
          }
      
          # Distributor
          location = /api/prom/push {
            
            proxy_pass       http://loki-write.default.svc.cluster.local:3100$request_uri;
          }
          location = /loki/api/v1/push {
            
            proxy_pass       http://loki-write.default.svc.cluster.local:3100$request_uri;
          }
          location = /distributor/ring {
            
            proxy_pass       http://loki-write.default.svc.cluster.local:3100$request_uri;
          }
          location = /otlp/v1/logs {
            
            proxy_pass       http://loki-write.default.svc.cluster.local:3100$request_uri;
          }
      
          # Ingester
          location = /flush {
            
            proxy_pass       http://loki-write.default.svc.cluster.local:3100$request_uri;
          }
          location ^~ /ingester/ {
            
            proxy_pass       http://loki-write.default.svc.cluster.local:3100$request_uri;
          }
          location = /ingester {
            
            internal;        # to suppress 301
          }
      
          # Ring
          location = /ring {
            
            proxy_pass       http://loki-write.default.svc.cluster.local:3100$request_uri;
          }
      
          # MemberListKV
          location = /memberlist {
            
            proxy_pass       http://loki-write.default.svc.cluster.local:3100$request_uri;
          }
      
          # Ruler
          location = /ruler/ring {
            
            proxy_pass       http://loki-backend.default.svc.cluster.local:3100$request_uri;
          }
          location = /api/prom/rules {
            
            proxy_pass       http://loki-backend.default.svc.cluster.local:3100$request_uri;
          }
          location ^~ /api/prom/rules/ {
            
            proxy_pass       http://loki-backend.default.svc.cluster.local:3100$request_uri;
          }
          location = /loki/api/v1/rules {
            
            proxy_pass       http://loki-backend.default.svc.cluster.local:3100$request_uri;
          }
          location ^~ /loki/api/v1/rules/ {
            
            proxy_pass       http://loki-backend.default.svc.cluster.local:3100$request_uri;
          }
          location = /prometheus/api/v1/alerts {
            
            proxy_pass       http://loki-backend.default.svc.cluster.local:3100$request_uri;
          }
          location = /prometheus/api/v1/rules {
            
            proxy_pass       http://loki-backend.default.svc.cluster.local:3100$request_uri;
          }
      
          # Compactor
          location = /compactor/ring {
            
            proxy_pass       http://loki-backend.default.svc.cluster.local:3100$request_uri;
          }
          location = /loki/api/v1/delete {
            
            proxy_pass       http://loki-backend.default.svc.cluster.local:3100$request_uri;
          }
          location = /loki/api/v1/cache/generation_numbers {
            
            proxy_pass       http://loki-backend.default.svc.cluster.local:3100$request_uri;
          }
      
          # IndexGateway
          location = /indexgateway/ring {
            
            proxy_pass       http://loki-backend.default.svc.cluster.local:3100$request_uri;
          }
      
          # QueryScheduler
          location = /scheduler/ring {
            
            proxy_pass       http://loki-backend.default.svc.cluster.local:3100$request_uri;
          }
      
          # Config
          location = /config {
            
            proxy_pass       http://loki-write.default.svc.cluster.local:3100$request_uri;
          }
      
      
          # QueryFrontend, Querier
          location = /api/prom/tail {
            proxy_set_header Upgrade $http_upgrade;
            proxy_set_header Connection "upgrade";
            
            proxy_pass       http://loki-read.default.svc.cluster.local:3100$request_uri;
          }
          location = /loki/api/v1/tail {
            proxy_set_header Upgrade $http_upgrade;
            proxy_set_header Connection "upgrade";
            
            proxy_pass       http://loki-read.default.svc.cluster.local:3100$request_uri;
          }
          location ^~ /api/prom/ {
            
            proxy_pass       http://loki-read.default.svc.cluster.local:3100$request_uri;
          }
          location = /api/prom {
            
            internal;        # to suppress 301
          }
          location ^~ /loki/api/v1/ {
            # pass custom headers set by Grafana as X-Query-Tags which are logged as key/value pairs in metrics.go log messages
            proxy_set_header X-Query-Tags "${query_tags},user=${http_x_grafana_user},dashboard_id=${http_x_dashboard_uid},dashboard_title=${http_x_dashboard_title},panel_id=${http_x_panel_id},panel_title=${http_x_panel_title},source_rule_uid=${http_x_rule_uid},rule_name=${http_x_rule_name},rule_folder=${http_x_rule_folder},rule_version=${http_x_rule_version},rule_source=${http_x_rule_source},rule_type=${http_x_rule_type}";
            
            proxy_pass       http://loki-read.default.svc.cluster.local:3100$request_uri;
          }
          location = /loki/api/v1 {
            
            internal;        # to suppress 301
          }
        }
      }
default, loki-release-gateway, Deployment (apps) has changed:
  # Source: loki/templates/gateway/deployment-gateway-nginx.yaml
  apiVersion: apps/v1
  kind: Deployment
  metadata:
    name: loki-release-gateway
    namespace: default
    labels:
      helm.sh/chart: loki-6.42.0
      app.kubernetes.io/name: loki
      app.kubernetes.io/instance: loki-release
      app.kubernetes.io/version: "3.5.5"
      app.kubernetes.io/component: gateway
  spec:
    replicas: 1
    strategy:
      type: RollingUpdate
    revisionHistoryLimit: 10
    selector:
      matchLabels:
        app.kubernetes.io/name: loki
        app.kubernetes.io/instance: loki-release
        app.kubernetes.io/component: gateway
    template:
      metadata:
        annotations:
-         checksum/config: 581c2a9782b0c6b8de8c6c69a01843ca30d81a1382ebd1ae915913017c9d968f
+         checksum/config: ecbb4c11aa9b4964a7b820c403987fd36eade50462bb97d94b137186a441fa33
        labels:
          app.kubernetes.io/name: loki
          app.kubernetes.io/instance: loki-release
          app.kubernetes.io/component: gateway
      spec:
        serviceAccountName: loki
        enableServiceLinks: true
        
        securityContext:
          fsGroup: 101
          runAsGroup: 101
          runAsNonRoot: true
          runAsUser: 101
        terminationGracePeriodSeconds: 30
        containers:
          - name: nginx
            image: docker.io/nginxinc/nginx-unprivileged:1.29-alpine
            imagePullPolicy: IfNotPresent
            ports:
              - name: http-metrics
                containerPort: 8080
                protocol: TCP
            readinessProbe:
              httpGet:
                path: /
                port: http-metrics
              initialDelaySeconds: 15
              timeoutSeconds: 1
            securityContext:
              allowPrivilegeEscalation: false
              capabilities:
                drop:
                - ALL
              readOnlyRootFilesystem: true
            volumeMounts:
              - name: config
                mountPath: /etc/nginx
              - name: tmp
                mountPath: /tmp
              - name: docker-entrypoint-d-override
                mountPath: /docker-entrypoint.d
            resources:
              {}
        affinity:
          podAntiAffinity:
            requiredDuringSchedulingIgnoredDuringExecution:
            - labelSelector:
                matchLabels:
                  app.kubernetes.io/component: gateway
                  app.kubernetes.io/instance: 'loki-release'
                  app.kubernetes.io/name: 'loki'
              topologyKey: kubernetes.io/hostname
        volumes:
          - name: config
            configMap:
              name: loki-release-gateway
          - name: tmp
            emptyDir: {}
          - name: docker-entrypoint-d-override
            emptyDir: {}
Distributed Scenario-diff-output
default, loki-release-gateway, ConfigMap (v1) has changed:
  # Source: loki/templates/gateway/configmap-gateway.yaml
  apiVersion: v1
  kind: ConfigMap
  metadata:
    name: loki-release-gateway
    namespace: default
    labels:
      helm.sh/chart: loki-6.42.0
      app.kubernetes.io/name: loki
      app.kubernetes.io/instance: loki-release
      app.kubernetes.io/version: "3.5.5"
      app.kubernetes.io/component: gateway
  data:
    nginx.conf: |
      worker_processes  5;  ## Default: 1
      error_log  /dev/stderr;
      pid        /tmp/nginx.pid;
      worker_rlimit_nofile 8192;
      
      events {
        worker_connections  4096;  ## Default: 1024
      }
      
      http {
        client_body_temp_path /tmp/client_temp;
        proxy_temp_path       /tmp/proxy_temp_path;
        fastcgi_temp_path     /tmp/fastcgi_temp;
        uwsgi_temp_path       /tmp/uwsgi_temp;
        scgi_temp_path        /tmp/scgi_temp;
      
        client_max_body_size  4M;
      
        proxy_read_timeout    600; ## 10 minutes
        proxy_send_timeout    600;
        proxy_connect_timeout 600;
      
        proxy_http_version    1.1;
      
        default_type application/octet-stream;
        log_format   main '$remote_addr - $remote_user [$time_local]  $status '
              '"$request" $body_bytes_sent "$http_referer" '
              '"$http_user_agent" "$http_x_forwarded_for"';
        access_log   /dev/stderr  main;
      
        sendfile     on;
        tcp_nopush   on;
        resolver kube-dns.kube-system.svc.cluster.local.;
      
        # if the X-Query-Tags header is empty, set a noop= without a value as empty values are not logged
        map $http_x_query_tags $query_tags {
          ""        "noop=";            # When header is empty, set noop=
          default   $http_x_query_tags; # Otherwise, preserve the original value
        }
      
        server {
          listen             8080;
          listen             [::]:8080;
      
          location = / {
            
            return 200 'OK';
            auth_basic off;
          }
      
          ########################################################
          # Configure backend targets
          location ^~ /ui {
            
-           proxy_pass       http://loki-release-distributor.default.svc.cluster.local:3100$request_uri;
+           proxy_pass       http://http.loki-release-querier.svc.default:3100%!(EXTRA string=cluster.local, string=3100)$request_uri;
          }
      
          # Distributor
          location = /api/prom/push {
            
            proxy_pass       http://loki-release-distributor.default.svc.cluster.local:3100$request_uri;
          }
          location = /loki/api/v1/push {
            
            proxy_pass       http://loki-release-distributor.default.svc.cluster.local:3100$request_uri;
          }
          location = /distributor/ring {
            
            proxy_pass       http://loki-release-distributor.default.svc.cluster.local:3100$request_uri;
          }
          location = /otlp/v1/logs {
            
            proxy_pass       http://loki-release-distributor.default.svc.cluster.local:3100$request_uri;
          }
      
          # Ingester
          location = /flush {
            
            proxy_pass       http://loki-release-ingester.default.svc.cluster.local:3100$request_uri;
          }
          location ^~ /ingester/ {
            
            proxy_pass       http://loki-release-ingester.default.svc.cluster.local:3100$request_uri;
          }
          location = /ingester {
            
            internal;        # to suppress 301
          }
      
          # Ring
          location = /ring {
            
            proxy_pass       http://loki-release-ingester.default.svc.cluster.local:3100$request_uri;
          }
      
          # MemberListKV
          location = /memberlist {
            
            proxy_pass       http://loki-release-ingester.default.svc.cluster.local:3100$request_uri;
          }
      
          # Ruler
          location = /ruler/ring {
            
            proxy_pass       http://loki-release-ruler.default.svc.cluster.local:3100$request_uri;
          }
          location = /api/prom/rules {
            
            proxy_pass       http://loki-release-ruler.default.svc.cluster.local:3100$request_uri;
          }
          location ^~ /api/prom/rules/ {
            
            proxy_pass       http://loki-release-ruler.default.svc.cluster.local:3100$request_uri;
          }
          location = /loki/api/v1/rules {
            
            proxy_pass       http://loki-release-ruler.default.svc.cluster.local:3100$request_uri;
          }
          location ^~ /loki/api/v1/rules/ {
            
            proxy_pass       http://loki-release-ruler.default.svc.cluster.local:3100$request_uri;
          }
          location = /prometheus/api/v1/alerts {
            
            proxy_pass       http://loki-release-ruler.default.svc.cluster.local:3100$request_uri;
          }
          location = /prometheus/api/v1/rules {
            
            proxy_pass       http://loki-release-ruler.default.svc.cluster.local:3100$request_uri;
          }
      
          # Compactor
          location = /compactor/ring {
            
            proxy_pass       http://loki-release-compactor.default.svc.cluster.local:3100$request_uri;
          }
          location = /loki/api/v1/delete {
            
            proxy_pass       http://loki-release-compactor.default.svc.cluster.local:3100$request_uri;
          }
          location = /loki/api/v1/cache/generation_numbers {
            
            proxy_pass       http://loki-release-compactor.default.svc.cluster.local:3100$request_uri;
          }
      
          # IndexGateway
          location = /indexgateway/ring {
            
            proxy_pass       http://loki-release-index-gateway.default.svc.cluster.local:3100$request_uri;
          }
      
          # QueryScheduler
          location = /scheduler/ring {
            
            proxy_pass       http://loki-release-query-scheduler.default.svc.cluster.local:3100$request_uri;
          }
      
          # Config
          location = /config {
            
            proxy_pass       http://loki-release-ingester.default.svc.cluster.local:3100$request_uri;
          }
      
      
          # QueryFrontend, Querier
          location = /api/prom/tail {
            proxy_set_header Upgrade $http_upgrade;
            proxy_set_header Connection "upgrade";
            
            proxy_pass       http://loki-release-query-frontend.default.svc.cluster.local:3100$request_uri;
          }
          location = /loki/api/v1/tail {
            proxy_set_header Upgrade $http_upgrade;
            proxy_set_header Connection "upgrade";
            
            proxy_pass       http://loki-release-query-frontend.default.svc.cluster.local:3100$request_uri;
          }
          location ^~ /api/prom/ {
            
            proxy_pass       http://loki-release-query-frontend.default.svc.cluster.local:3100$request_uri;
          }
          location = /api/prom {
            
            internal;        # to suppress 301
          }
          location ^~ /loki/api/v1/ {
            # pass custom headers set by Grafana as X-Query-Tags which are logged as key/value pairs in metrics.go log messages
            proxy_set_header X-Query-Tags "${query_tags},user=${http_x_grafana_user},dashboard_id=${http_x_dashboard_uid},dashboard_title=${http_x_dashboard_title},panel_id=${http_x_panel_id},panel_title=${http_x_panel_title},source_rule_uid=${http_x_rule_uid},rule_name=${http_x_rule_name},rule_folder=${http_x_rule_folder},rule_version=${http_x_rule_version},rule_source=${http_x_rule_source},rule_type=${http_x_rule_type}";
            
            proxy_pass       http://loki-release-query-frontend.default.svc.cluster.local:3100$request_uri;
          }
          location = /loki/api/v1 {
            
            internal;        # to suppress 301
          }
        }
      }
default, loki-release-gateway, Deployment (apps) has changed:
  # Source: loki/templates/gateway/deployment-gateway-nginx.yaml
  apiVersion: apps/v1
  kind: Deployment
  metadata:
    name: loki-release-gateway
    namespace: default
    labels:
      helm.sh/chart: loki-6.42.0
      app.kubernetes.io/name: loki
      app.kubernetes.io/instance: loki-release
      app.kubernetes.io/version: "3.5.5"
      app.kubernetes.io/component: gateway
  spec:
    replicas: 1
    strategy:
      type: RollingUpdate
    revisionHistoryLimit: 10
    selector:
      matchLabels:
        app.kubernetes.io/name: loki
        app.kubernetes.io/instance: loki-release
        app.kubernetes.io/component: gateway
    template:
      metadata:
        annotations:
-         checksum/config: f283388e1ff04ccba5362747ae2ced902d1cabb194de4c6ec1ab75fcd85a8015
+         checksum/config: 32c5a72e71ef82ee3c12ae6f3d80484b9aec188dfdd5b097fc21760c9e67e012
        labels:
          app.kubernetes.io/name: loki
          app.kubernetes.io/instance: loki-release
          app.kubernetes.io/component: gateway
      spec:
        serviceAccountName: loki
        enableServiceLinks: true
        
        securityContext:
          fsGroup: 101
          runAsGroup: 101
          runAsNonRoot: true
          runAsUser: 101
        terminationGracePeriodSeconds: 30
        containers:
          - name: nginx
            image: docker.io/nginxinc/nginx-unprivileged:1.29-alpine
            imagePullPolicy: IfNotPresent
            ports:
              - name: http-metrics
                containerPort: 8080
                protocol: TCP
            readinessProbe:
              httpGet:
                path: /
                port: http-metrics
              initialDelaySeconds: 15
              timeoutSeconds: 1
            securityContext:
              allowPrivilegeEscalation: false
              capabilities:
                drop:
                - ALL
              readOnlyRootFilesystem: true
            volumeMounts:
              - name: config
                mountPath: /etc/nginx
              - name: tmp
                mountPath: /tmp
              - name: docker-entrypoint-d-override
                mountPath: /docker-entrypoint.d
            resources:
              {}
        affinity:
          podAntiAffinity:
            requiredDuringSchedulingIgnoredDuringExecution:
            - labelSelector:
                matchLabels:
                  app.kubernetes.io/component: gateway
                  app.kubernetes.io/instance: 'loki-release'
                  app.kubernetes.io/name: 'loki'
              topologyKey: kubernetes.io/hostname
        volumes:
          - name: config
            configMap:
              name: loki-release-gateway
          - name: tmp
            emptyDir: {}
          - name: docker-entrypoint-d-override
            emptyDir: {}
Ingress Values Scenario-diff-output
default, loki-read, Deployment (apps) has changed:
  # Source: loki/templates/read/deployment-read.yaml
  apiVersion: apps/v1
  kind: Deployment
  metadata:
    name: loki-read
    namespace: default
    labels:
      app.kubernetes.io/part-of: memberlist
      helm.sh/chart: loki-6.42.0
      app.kubernetes.io/name: loki
      app.kubernetes.io/instance: loki-release
      app.kubernetes.io/version: "3.5.5"
      app.kubernetes.io/component: read
  spec:
    replicas: 1
    strategy:
      rollingUpdate:
        maxSurge: 0
        maxUnavailable: 1
    revisionHistoryLimit: 10
    selector:
      matchLabels:
        app.kubernetes.io/name: loki
        app.kubernetes.io/instance: loki-release
        app.kubernetes.io/component: read
    template:
      metadata:
        annotations:
          checksum/config: afc4770faa248a744723fe521d644449dbc7de4a189a88072560a74feacd35fc
          prometheus.io/port: "3100"
          prometheus.io/scrape: "true"
        labels:
          app.kubernetes.io/part-of: memberlist
          app.kubernetes.io/name: loki
          app.kubernetes.io/instance: loki-release
          app.kubernetes.io/component: read
      spec:
        serviceAccountName: loki
        automountServiceAccountToken: true
        
        securityContext:
          fsGroup: 10001
          runAsGroup: 10001
          runAsNonRoot: true
          runAsUser: 10001
        terminationGracePeriodSeconds: 30
        containers:
          - name: loki
            image: docker.io/grafana/loki:3.5.5
            imagePullPolicy: IfNotPresent
            args:
              - -config.file=/etc/loki/config/config.yaml
-             - -target=read
-             - -legacy-read-mode=false
+             - -target=%sread- -legacy-read-mode=false
              - -common.compactor-grpc-address=loki-backend.default.svc.cluster.local:9095
            ports:
              - name: http-metrics
                containerPort: 3100
                protocol: TCP
              - name: grpc
                containerPort: 9095
                protocol: TCP
              - name: http-memberlist
                containerPort: 7946
                protocol: TCP
            securityContext:
              allowPrivilegeEscalation: false
              capabilities:
                drop:
                - ALL
              readOnlyRootFilesystem: true
            readinessProbe:
              httpGet:
                path: /ready
                port: http-metrics
              initialDelaySeconds: 30
              timeoutSeconds: 1
            volumeMounts:
              - name: config
                mountPath: /etc/loki/config
              - name: runtime-config
                mountPath: /etc/loki/runtime-config
              - name: tmp
                mountPath: /tmp
              - name: data
                mountPath: /var/loki
            resources:
              {}
        affinity:
          podAntiAffinity:
            requiredDuringSchedulingIgnoredDuringExecution:
            - labelSelector:
                matchLabels:
                  app.kubernetes.io/component: read
                  app.kubernetes.io/instance: 'loki-release'
                  app.kubernetes.io/name: 'loki'
              topologyKey: kubernetes.io/hostname
        volumes:
          - name: tmp
            emptyDir: {}
          - name: data
            emptyDir: {}
          - name: config
            configMap:
              name: loki
              items:
                - key: "config.yaml"
                  path: "config.yaml"
          - name: runtime-config
            configMap:
              name: loki-runtime
default, loki-release-gateway, ConfigMap (v1) has changed:
  # Source: loki/templates/gateway/configmap-gateway.yaml
  apiVersion: v1
  kind: ConfigMap
  metadata:
    name: loki-release-gateway
    namespace: default
    labels:
      helm.sh/chart: loki-6.42.0
      app.kubernetes.io/name: loki
      app.kubernetes.io/instance: loki-release
      app.kubernetes.io/version: "3.5.5"
      app.kubernetes.io/component: gateway
  data:
    nginx.conf: |
      worker_processes  5;  ## Default: 1
      error_log  /dev/stderr;
      pid        /tmp/nginx.pid;
      worker_rlimit_nofile 8192;
      
      events {
        worker_connections  4096;  ## Default: 1024
      }
      
      http {
        client_body_temp_path /tmp/client_temp;
        proxy_temp_path       /tmp/proxy_temp_path;
        fastcgi_temp_path     /tmp/fastcgi_temp;
        uwsgi_temp_path       /tmp/uwsgi_temp;
        scgi_temp_path        /tmp/scgi_temp;
      
        client_max_body_size  4M;
      
        proxy_read_timeout    600; ## 10 minutes
        proxy_send_timeout    600;
        proxy_connect_timeout 600;
      
        proxy_http_version    1.1;
      
        default_type application/octet-stream;
        log_format   main '$remote_addr - $remote_user [$time_local]  $status '
              '"$request" $body_bytes_sent "$http_referer" '
              '"$http_user_agent" "$http_x_forwarded_for"';
        access_log   /dev/stderr  main;
      
        sendfile     on;
        tcp_nopush   on;
        resolver kube-dns.kube-system.svc.cluster.local.;
      
        # if the X-Query-Tags header is empty, set a noop= without a value as empty values are not logged
        map $http_x_query_tags $query_tags {
          ""        "noop=";            # When header is empty, set noop=
          default   $http_x_query_tags; # Otherwise, preserve the original value
        }
      
        server {
          listen             8080;
          listen             [::]:8080;
      
          location = / {
            
            return 200 'OK';
            auth_basic off;
          }
      
          ########################################################
          # Configure backend targets
          location ^~ /ui {
            
-           proxy_pass       http://loki-write.default.svc.cluster.local:3100$request_uri;
+           proxy_pass       http://loki-read.default.svc.cluster.local:3100$request_uri;
          }
      
          # Distributor
          location = /api/prom/push {
            
            proxy_pass       http://loki-write.default.svc.cluster.local:3100$request_uri;
          }
          location = /loki/api/v1/push {
            
            proxy_pass       http://loki-write.default.svc.cluster.local:3100$request_uri;
          }
          location = /distributor/ring {
            
            proxy_pass       http://loki-write.default.svc.cluster.local:3100$request_uri;
          }
          location = /otlp/v1/logs {
            
            proxy_pass       http://loki-write.default.svc.cluster.local:3100$request_uri;
          }
      
          # Ingester
          location = /flush {
            
            proxy_pass       http://loki-write.default.svc.cluster.local:3100$request_uri;
          }
          location ^~ /ingester/ {
            
            proxy_pass       http://loki-write.default.svc.cluster.local:3100$request_uri;
          }
          location = /ingester {
            
            internal;        # to suppress 301
          }
      
          # Ring
          location = /ring {
            
            proxy_pass       http://loki-write.default.svc.cluster.local:3100$request_uri;
          }
      
          # MemberListKV
          location = /memberlist {
            
            proxy_pass       http://loki-write.default.svc.cluster.local:3100$request_uri;
          }
      
          # Ruler
          location = /ruler/ring {
            
            proxy_pass       http://loki-backend.default.svc.cluster.local:3100$request_uri;
          }
          location = /api/prom/rules {
            
            proxy_pass       http://loki-backend.default.svc.cluster.local:3100$request_uri;
          }
          location ^~ /api/prom/rules/ {
            
            proxy_pass       http://loki-backend.default.svc.cluster.local:3100$request_uri;
          }
          location = /loki/api/v1/rules {
            
            proxy_pass       http://loki-backend.default.svc.cluster.local:3100$request_uri;
          }
          location ^~ /loki/api/v1/rules/ {
            
            proxy_pass       http://loki-backend.default.svc.cluster.local:3100$request_uri;
          }
          location = /prometheus/api/v1/alerts {
            
            proxy_pass       http://loki-backend.default.svc.cluster.local:3100$request_uri;
          }
          location = /prometheus/api/v1/rules {
            
            proxy_pass       http://loki-backend.default.svc.cluster.local:3100$request_uri;
          }
      
          # Compactor
          location = /compactor/ring {
            
            proxy_pass       http://loki-backend.default.svc.cluster.local:3100$request_uri;
          }
          location = /loki/api/v1/delete {
            
            proxy_pass       http://loki-backend.default.svc.cluster.local:3100$request_uri;
          }
          location = /loki/api/v1/cache/generation_numbers {
            
            proxy_pass       http://loki-backend.default.svc.cluster.local:3100$request_uri;
          }
      
          # IndexGateway
          location = /indexgateway/ring {
            
            proxy_pass       http://loki-backend.default.svc.cluster.local:3100$request_uri;
          }
      
          # QueryScheduler
          location = /scheduler/ring {
            
            proxy_pass       http://loki-backend.default.svc.cluster.local:3100$request_uri;
          }
      
          # Config
          location = /config {
            
            proxy_pass       http://loki-write.default.svc.cluster.local:3100$request_uri;
          }
      
      
          # QueryFrontend, Querier
          location = /api/prom/tail {
            proxy_set_header Upgrade $http_upgrade;
            proxy_set_header Connection "upgrade";
            
            proxy_pass       http://loki-read.default.svc.cluster.local:3100$request_uri;
          }
          location = /loki/api/v1/tail {
            proxy_set_header Upgrade $http_upgrade;
            proxy_set_header Connection "upgrade";
            
            proxy_pass       http://loki-read.default.svc.cluster.local:3100$request_uri;
          }
          location ^~ /api/prom/ {
            
            proxy_pass       http://loki-read.default.svc.cluster.local:3100$request_uri;
          }
          location = /api/prom {
            
            internal;        # to suppress 301
          }
          location ^~ /loki/api/v1/ {
            # pass custom headers set by Grafana as X-Query-Tags which are logged as key/value pairs in metrics.go log messages
            proxy_set_header X-Query-Tags "${query_tags},user=${http_x_grafana_user},dashboard_id=${http_x_dashboard_uid},dashboard_title=${http_x_dashboard_title},panel_id=${http_x_panel_id},panel_title=${http_x_panel_title},source_rule_uid=${http_x_rule_uid},rule_name=${http_x_rule_name},rule_folder=${http_x_rule_folder},rule_version=${http_x_rule_version},rule_source=${http_x_rule_source},rule_type=${http_x_rule_type}";
            
            proxy_pass       http://loki-read.default.svc.cluster.local:3100$request_uri;
          }
          location = /loki/api/v1 {
            
            internal;        # to suppress 301
          }
        }
      }
default, loki-release-gateway, Deployment (apps) has changed:
  # Source: loki/templates/gateway/deployment-gateway-nginx.yaml
  apiVersion: apps/v1
  kind: Deployment
  metadata:
    name: loki-release-gateway
    namespace: default
    labels:
      helm.sh/chart: loki-6.42.0
      app.kubernetes.io/name: loki
      app.kubernetes.io/instance: loki-release
      app.kubernetes.io/version: "3.5.5"
      app.kubernetes.io/component: gateway
  spec:
    replicas: 1
    strategy:
      type: RollingUpdate
    revisionHistoryLimit: 10
    selector:
      matchLabels:
        app.kubernetes.io/name: loki
        app.kubernetes.io/instance: loki-release
        app.kubernetes.io/component: gateway
    template:
      metadata:
        annotations:
-         checksum/config: 581c2a9782b0c6b8de8c6c69a01843ca30d81a1382ebd1ae915913017c9d968f
+         checksum/config: ecbb4c11aa9b4964a7b820c403987fd36eade50462bb97d94b137186a441fa33
        labels:
          app.kubernetes.io/name: loki
          app.kubernetes.io/instance: loki-release
          app.kubernetes.io/component: gateway
      spec:
        serviceAccountName: loki
        enableServiceLinks: true
        
        securityContext:
          fsGroup: 101
          runAsGroup: 101
          runAsNonRoot: true
          runAsUser: 101
        terminationGracePeriodSeconds: 30
        containers:
          - name: nginx
            image: docker.io/nginxinc/nginx-unprivileged:1.29-alpine
            imagePullPolicy: IfNotPresent
            ports:
              - name: http-metrics
                containerPort: 8080
                protocol: TCP
            readinessProbe:
              httpGet:
                path: /
                port: http-metrics
              initialDelaySeconds: 15
              timeoutSeconds: 1
            securityContext:
              allowPrivilegeEscalation: false
              capabilities:
                drop:
                - ALL
              readOnlyRootFilesystem: true
            volumeMounts:
              - name: config
                mountPath: /etc/nginx
              - name: tmp
                mountPath: /tmp
              - name: docker-entrypoint-d-override
                mountPath: /docker-entrypoint.d
            resources:
              {}
        affinity:
          podAntiAffinity:
            requiredDuringSchedulingIgnoredDuringExecution:
            - labelSelector:
                matchLabels:
                  app.kubernetes.io/component: gateway
                  app.kubernetes.io/instance: 'loki-release'
                  app.kubernetes.io/name: 'loki'
              topologyKey: kubernetes.io/hostname
        volumes:
          - name: config
            configMap:
              name: loki-release-gateway
          - name: tmp
            emptyDir: {}
          - name: docker-entrypoint-d-override
            emptyDir: {}
Legacy Monitoring Values Scenario-diff-output
default, loki-read, Deployment (apps) has changed:
  # Source: loki/templates/read/deployment-read.yaml
  apiVersion: apps/v1
  kind: Deployment
  metadata:
    name: loki-read
    namespace: default
    labels:
      app.kubernetes.io/part-of: memberlist
      helm.sh/chart: loki-6.42.0
      app.kubernetes.io/name: loki
      app.kubernetes.io/instance: loki-release
      app.kubernetes.io/version: "3.5.5"
      app.kubernetes.io/component: read
  spec:
    replicas: 1
    strategy:
      rollingUpdate:
        maxSurge: 0
        maxUnavailable: 1
    revisionHistoryLimit: 10
    selector:
      matchLabels:
        app.kubernetes.io/name: loki
        app.kubernetes.io/instance: loki-release
        app.kubernetes.io/component: read
    template:
      metadata:
        annotations:
          checksum/config: afc4770faa248a744723fe521d644449dbc7de4a189a88072560a74feacd35fc
          prometheus.io/port: "3100"
          prometheus.io/scrape: "true"
        labels:
          app.kubernetes.io/part-of: memberlist
          app.kubernetes.io/name: loki
          app.kubernetes.io/instance: loki-release
          app.kubernetes.io/component: read
      spec:
        serviceAccountName: loki
        automountServiceAccountToken: true
        
        securityContext:
          fsGroup: 10001
          runAsGroup: 10001
          runAsNonRoot: true
          runAsUser: 10001
        terminationGracePeriodSeconds: 30
        containers:
          - name: loki
            image: docker.io/grafana/loki:3.5.5
            imagePullPolicy: IfNotPresent
            args:
              - -config.file=/etc/loki/config/config.yaml
-             - -target=read
-             - -legacy-read-mode=false
+             - -target=%sread- -legacy-read-mode=false
              - -common.compactor-grpc-address=loki-backend.default.svc.cluster.local:9095
            ports:
              - name: http-metrics
                containerPort: 3100
                protocol: TCP
              - name: grpc
                containerPort: 9095
                protocol: TCP
              - name: http-memberlist
                containerPort: 7946
                protocol: TCP
            securityContext:
              allowPrivilegeEscalation: false
              capabilities:
                drop:
                - ALL
              readOnlyRootFilesystem: true
            readinessProbe:
              httpGet:
                path: /ready
                port: http-metrics
              initialDelaySeconds: 30
              timeoutSeconds: 1
            volumeMounts:
              - name: config
                mountPath: /etc/loki/config
              - name: runtime-config
                mountPath: /etc/loki/runtime-config
              - name: tmp
                mountPath: /tmp
              - name: data
                mountPath: /var/loki
            resources:
              {}
        affinity:
          podAntiAffinity:
            requiredDuringSchedulingIgnoredDuringExecution:
            - labelSelector:
                matchLabels:
                  app.kubernetes.io/component: read
                  app.kubernetes.io/instance: 'loki-release'
                  app.kubernetes.io/name: 'loki'
              topologyKey: kubernetes.io/hostname
        volumes:
          - name: tmp
            emptyDir: {}
          - name: data
            emptyDir: {}
          - name: config
            configMap:
              name: loki
              items:
                - key: "config.yaml"
                  path: "config.yaml"
          - name: runtime-config
            configMap:
              name: loki-runtime
default, loki-release-gateway, ConfigMap (v1) has changed:
  # Source: loki/templates/gateway/configmap-gateway.yaml
  apiVersion: v1
  kind: ConfigMap
  metadata:
    name: loki-release-gateway
    namespace: default
    labels:
      helm.sh/chart: loki-6.42.0
      app.kubernetes.io/name: loki
      app.kubernetes.io/instance: loki-release
      app.kubernetes.io/version: "3.5.5"
      app.kubernetes.io/component: gateway
  data:
    nginx.conf: |
      worker_processes  5;  ## Default: 1
      error_log  /dev/stderr;
      pid        /tmp/nginx.pid;
      worker_rlimit_nofile 8192;
      
      events {
        worker_connections  4096;  ## Default: 1024
      }
      
      http {
        client_body_temp_path /tmp/client_temp;
        proxy_temp_path       /tmp/proxy_temp_path;
        fastcgi_temp_path     /tmp/fastcgi_temp;
        uwsgi_temp_path       /tmp/uwsgi_temp;
        scgi_temp_path        /tmp/scgi_temp;
      
        client_max_body_size  4M;
      
        proxy_read_timeout    600; ## 10 minutes
        proxy_send_timeout    600;
        proxy_connect_timeout 600;
      
        proxy_http_version    1.1;
      
        default_type application/octet-stream;
        log_format   main '$remote_addr - $remote_user [$time_local]  $status '
              '"$request" $body_bytes_sent "$http_referer" '
              '"$http_user_agent" "$http_x_forwarded_for"';
        access_log   /dev/stderr  main;
      
        sendfile     on;
        tcp_nopush   on;
        resolver kube-dns.kube-system.svc.cluster.local.;
      
        # if the X-Query-Tags header is empty, set a noop= without a value as empty values are not logged
        map $http_x_query_tags $query_tags {
          ""        "noop=";            # When header is empty, set noop=
          default   $http_x_query_tags; # Otherwise, preserve the original value
        }
      
        server {
          listen             8080;
          listen             [::]:8080;
      
          location = / {
            
            return 200 'OK';
            auth_basic off;
          }
      
          ########################################################
          # Configure backend targets
          location ^~ /ui {
            
-           proxy_pass       http://loki-write.default.svc.cluster.local:3100$request_uri;
+           proxy_pass       http://loki-read.default.svc.cluster.local:3100$request_uri;
          }
      
          # Distributor
          location = /api/prom/push {
            
            proxy_pass       http://loki-write.default.svc.cluster.local:3100$request_uri;
          }
          location = /loki/api/v1/push {
            
            proxy_pass       http://loki-write.default.svc.cluster.local:3100$request_uri;
          }
          location = /distributor/ring {
            
            proxy_pass       http://loki-write.default.svc.cluster.local:3100$request_uri;
          }
          location = /otlp/v1/logs {
            
            proxy_pass       http://loki-write.default.svc.cluster.local:3100$request_uri;
          }
      
          # Ingester
          location = /flush {
            
            proxy_pass       http://loki-write.default.svc.cluster.local:3100$request_uri;
          }
          location ^~ /ingester/ {
            
            proxy_pass       http://loki-write.default.svc.cluster.local:3100$request_uri;
          }
          location = /ingester {
            
            internal;        # to suppress 301
          }
      
          # Ring
          location = /ring {
            
            proxy_pass       http://loki-write.default.svc.cluster.local:3100$request_uri;
          }
      
          # MemberListKV
          location = /memberlist {
            
            proxy_pass       http://loki-write.default.svc.cluster.local:3100$request_uri;
          }
      
          # Ruler
          location = /ruler/ring {
            
            proxy_pass       http://loki-backend.default.svc.cluster.local:3100$request_uri;
          }
          location = /api/prom/rules {
            
            proxy_pass       http://loki-backend.default.svc.cluster.local:3100$request_uri;
          }
          location ^~ /api/prom/rules/ {
            
            proxy_pass       http://loki-backend.default.svc.cluster.local:3100$request_uri;
          }
          location = /loki/api/v1/rules {
            
            proxy_pass       http://loki-backend.default.svc.cluster.local:3100$request_uri;
          }
          location ^~ /loki/api/v1/rules/ {
            
            proxy_pass       http://loki-backend.default.svc.cluster.local:3100$request_uri;
          }
          location = /prometheus/api/v1/alerts {
            
            proxy_pass       http://loki-backend.default.svc.cluster.local:3100$request_uri;
          }
          location = /prometheus/api/v1/rules {
            
            proxy_pass       http://loki-backend.default.svc.cluster.local:3100$request_uri;
          }
      
          # Compactor
          location = /compactor/ring {
            
            proxy_pass       http://loki-backend.default.svc.cluster.local:3100$request_uri;
          }
          location = /loki/api/v1/delete {
            
            proxy_pass       http://loki-backend.default.svc.cluster.local:3100$request_uri;
          }
          location = /loki/api/v1/cache/generation_numbers {
            
            proxy_pass       http://loki-backend.default.svc.cluster.local:3100$request_uri;
          }
      
          # IndexGateway
          location = /indexgateway/ring {
            
            proxy_pass       http://loki-backend.default.svc.cluster.local:3100$request_uri;
          }
      
          # QueryScheduler
          location = /scheduler/ring {
            
            proxy_pass       http://loki-backend.default.svc.cluster.local:3100$request_uri;
          }
      
          # Config
          location = /config {
            
            proxy_pass       http://loki-write.default.svc.cluster.local:3100$request_uri;
          }
      
      
          # QueryFrontend, Querier
          location = /api/prom/tail {
            proxy_set_header Upgrade $http_upgrade;
            proxy_set_header Connection "upgrade";
            
            proxy_pass       http://loki-read.default.svc.cluster.local:3100$request_uri;
          }
          location = /loki/api/v1/tail {
            proxy_set_header Upgrade $http_upgrade;
            proxy_set_header Connection "upgrade";
            
            proxy_pass       http://loki-read.default.svc.cluster.local:3100$request_uri;
          }
          location ^~ /api/prom/ {
            
            proxy_pass       http://loki-read.default.svc.cluster.local:3100$request_uri;
          }
          location = /api/prom {
            
            internal;        # to suppress 301
          }
          location ^~ /loki/api/v1/ {
            # pass custom headers set by Grafana as X-Query-Tags which are logged as key/value pairs in metrics.go log messages
            proxy_set_header X-Query-Tags "${query_tags},user=${http_x_grafana_user},dashboard_id=${http_x_dashboard_uid},dashboard_title=${http_x_dashboard_title},panel_id=${http_x_panel_id},panel_title=${http_x_panel_title},source_rule_uid=${http_x_rule_uid},rule_name=${http_x_rule_name},rule_folder=${http_x_rule_folder},rule_version=${http_x_rule_version},rule_source=${http_x_rule_source},rule_type=${http_x_rule_type}";
            
            proxy_pass       http://loki-read.default.svc.cluster.local:3100$request_uri;
          }
          location = /loki/api/v1 {
            
            internal;        # to suppress 301
          }
        }
      }
default, loki-release-gateway, Deployment (apps) has changed:
  # Source: loki/templates/gateway/deployment-gateway-nginx.yaml
  apiVersion: apps/v1
  kind: Deployment
  metadata:
    name: loki-release-gateway
    namespace: default
    labels:
      helm.sh/chart: loki-6.42.0
      app.kubernetes.io/name: loki
      app.kubernetes.io/instance: loki-release
      app.kubernetes.io/version: "3.5.5"
      app.kubernetes.io/component: gateway
  spec:
    replicas: 1
    strategy:
      type: RollingUpdate
    revisionHistoryLimit: 10
    selector:
      matchLabels:
        app.kubernetes.io/name: loki
        app.kubernetes.io/instance: loki-release
        app.kubernetes.io/component: gateway
    template:
      metadata:
        annotations:
-         checksum/config: 581c2a9782b0c6b8de8c6c69a01843ca30d81a1382ebd1ae915913017c9d968f
+         checksum/config: ecbb4c11aa9b4964a7b820c403987fd36eade50462bb97d94b137186a441fa33
        labels:
          app.kubernetes.io/name: loki
          app.kubernetes.io/instance: loki-release
          app.kubernetes.io/component: gateway
      spec:
        serviceAccountName: loki
        enableServiceLinks: true
        
        securityContext:
          fsGroup: 101
          runAsGroup: 101
          runAsNonRoot: true
          runAsUser: 101
        terminationGracePeriodSeconds: 30
        containers:
          - name: nginx
            image: docker.io/nginxinc/nginx-unprivileged:1.29-alpine
            imagePullPolicy: IfNotPresent
            ports:
              - name: http-metrics
                containerPort: 8080
                protocol: TCP
            readinessProbe:
              httpGet:
                path: /
                port: http-metrics
              initialDelaySeconds: 15
              timeoutSeconds: 1
            securityContext:
              allowPrivilegeEscalation: false
              capabilities:
                drop:
                - ALL
              readOnlyRootFilesystem: true
            volumeMounts:
              - name: config
                mountPath: /etc/nginx
              - name: tmp
                mountPath: /tmp
              - name: docker-entrypoint-d-override
                mountPath: /docker-entrypoint.d
            resources:
              {}
        affinity:
          podAntiAffinity:
            requiredDuringSchedulingIgnoredDuringExecution:
            - labelSelector:
                matchLabels:
                  app.kubernetes.io/component: gateway
                  app.kubernetes.io/instance: 'loki-release'
                  app.kubernetes.io/name: 'loki'
              topologyKey: kubernetes.io/hostname
        volumes:
          - name: config
            configMap:
              name: loki-release-gateway
          - name: tmp
            emptyDir: {}
          - name: docker-entrypoint-d-override
            emptyDir: {}
Simple Scalable AWS Kube IRSA Values Scenario-diff-output
867c867,868
<             - -target=%sread- -legacy-read-mode=false
---
>             - -target=read
>             - -legacy-read-mode=false
Simple Thanos Values Scenario-diff-output
1329c1329,1330
<             - -target=%sread- -legacy-read-mode=false
---
>             - -target=read
>             - -legacy-read-mode=false
Single Binary Scenario-diff-output
default, loki-release, StatefulSet (apps) has changed:
  # Source: loki/templates/single-binary/statefulset.yaml
  apiVersion: apps/v1
  kind: StatefulSet
  metadata:
    name: loki-release
    namespace: default
    labels:
      helm.sh/chart: loki-6.42.0
      app.kubernetes.io/name: loki
      app.kubernetes.io/instance: loki-release
      app.kubernetes.io/version: "3.5.5"
      app.kubernetes.io/component: single-binary
      app.kubernetes.io/part-of: memberlist
  spec:
    replicas: 1
    podManagementPolicy: Parallel
    updateStrategy:
      rollingUpdate:
        partition: 0
    serviceName: loki-release-headless
    revisionHistoryLimit: 10
    
    persistentVolumeClaimRetentionPolicy:
      whenDeleted: Delete
      whenScaled: Delete
    selector:
      matchLabels:
        app.kubernetes.io/name: loki
        app.kubernetes.io/instance: loki-release
        app.kubernetes.io/component: single-binary
    template:
      metadata:
        annotations:
          checksum/config: fa7d9b77a672d0455e9b3e51875e4e463408fb25efbc7c8e94424d0b0bd5720b
          kubectl.kubernetes.io/default-container: "loki"
        labels:
          app.kubernetes.io/name: loki
          app.kubernetes.io/instance: loki-release
          app.kubernetes.io/component: single-binary
          app.kubernetes.io/part-of: memberlist
      spec:
        serviceAccountName: loki
        automountServiceAccountToken: true
        enableServiceLinks: true
        
        securityContext:
          fsGroup: 10001
          runAsGroup: 10001
          runAsNonRoot: true
          runAsUser: 10001
        terminationGracePeriodSeconds: 30
        containers:
          - name: loki
            image: docker.io/grafana/loki:3.5.5
            imagePullPolicy: IfNotPresent
            args:
              - -config.file=/etc/loki/config/config.yaml
-             - -target=all
+             - -target=%sall
            ports:
              - name: http-metrics
                containerPort: 3100
                protocol: TCP
              - name: grpc
                containerPort: 9095
                protocol: TCP
              - name: http-memberlist
                containerPort: 7946
                protocol: TCP
            securityContext:
              allowPrivilegeEscalation: false
              capabilities:
                drop:
                - ALL
              readOnlyRootFilesystem: true
            readinessProbe:
              httpGet:
                path: /ready
                port: http-metrics
              initialDelaySeconds: 30
              timeoutSeconds: 1
            volumeMounts:
              - name: tmp
                mountPath: /tmp
              - name: config
                mountPath: /etc/loki/config
              - name: runtime-config
                mountPath: /etc/loki/runtime-config
              - name: storage
                mountPath: /var/loki
              - name: sc-rules-volume
                mountPath: "/rules"
            resources:
              {}
          - name: loki-sc-rules
            image: "docker.io/kiwigrid/k8s-sidecar:1.30.10"
            imagePullPolicy: IfNotPresent
            env:
              - name: METHOD
                value: WATCH
              - name: LABEL
                value: "loki_rule"
              - name: FOLDER
                value: "/rules"
              - name: RESOURCE
                value: "both"
              - name: WATCH_SERVER_TIMEOUT
                value: "60"
              - name: WATCH_CLIENT_TIMEOUT
                value: "60"
              - name: LOG_LEVEL
                value: "INFO"
            securityContext:
              allowPrivilegeEscalation: false
              capabilities:
                drop:
                - ALL
              readOnlyRootFilesystem: true
            volumeMounts:
              - name: sc-rules-volume
                mountPath: "/rules"
        affinity:
          podAntiAffinity:
            requiredDuringSchedulingIgnoredDuringExecution:
            - labelSelector:
                matchLabels:
                  app.kubernetes.io/component: single-binary
                  app.kubernetes.io/instance: 'loki-release'
                  app.kubernetes.io/name: 'loki'
              topologyKey: kubernetes.io/hostname
        volumes:
          - name: tmp
            emptyDir: {}
          - name: config
            configMap:
              name: loki
              items:
                - key: "config.yaml"
                  path: "config.yaml"
          - name: runtime-config
            configMap:
              name: loki-runtime
          - name: sc-rules-volume
            emptyDir: {}
    volumeClaimTemplates:
      - apiVersion: v1
        kind: PersistentVolumeClaim
        metadata:
          name: storage
        spec:
          accessModes:
            - ReadWriteOnce
          resources:
            requests:
              storage: "10Gi"
@trevorwhitney
trevorwhitney enabled auto-merge (squash) October 7, 2025 23:49
@trevorwhitney
trevorwhitney merged commit 3a1e5ad into main Oct 7, 2025
77 of 81 checks passed
@trevorwhitney
trevorwhitney deleted the twhitney/operational-ui-hackathon branch October 7, 2025 23:52
@fcjack fcjack mentioned this pull request Oct 8, 2025
6 tasks
@rsommer

rsommer commented Nov 18, 2025

Copy link
Copy Markdown

LGTM, though we might want to flag this as a breaking change to the configuration since people seem to be relying on the UI?

That would have been nice :( Further more, the referenced operational UI link in the description of the pull request leads to a 404 page.

@chewrocca

Copy link
Copy Markdown

https://github.com/grafana/loki-operational-ui seems like it might not be publically available? Getting 404.

@JStickler

Copy link
Copy Markdown
Contributor

@chewrocca following up with the team to find out for you.

@trevorwhitney

Copy link
Copy Markdown
Collaborator Author

@rsommer sorry about that, the feat! in the commit should have denoted it as a breaking change, but our changelog automation broke for the 3.6 release and that must have been missed in the manual recreation of the changelog.

it does look like the repo hasn't been made public yet, I will look into open sourcing it. in the meantime it should be available in the plugin catalog, id is grafana-lokioperational-app. please let me know if you still have trouble accessing it.

@chewrocca

Copy link
Copy Markdown

Unfortunately, I'm getting Error: ✗ 404: Plugin not found when I try grafana cli plugins install grafana-lokioperational-app

@ondrejmo

Copy link
Copy Markdown

Unfortunately, I'm getting Error: ✗ 404: Plugin not found when I try grafana cli plugins install grafana-lokioperational-app

It seems to work now.

grafana@grafana-5c4548f6f8-sz4qj:/usr/share/grafana$ grafana cli plugins install grafana-lokioperational-app
INFO [11-22|09:18:23] Starting Grafana                         logger=settings version=12.3.0 commit=20051fb1fc604fc54aae76356da1c14612af41d0 branch=release-12.3.0 compiled=2025-11-22T09:18:23+01:00
INFO [11-22|09:18:23] Config loaded from                       logger=settings file=/usr/share/grafana/conf/defaults.ini
INFO [11-22|09:18:23] Config overridden from Environment variable logger=settings var="GF_PATHS_DATA=/var/lib/grafana"
INFO [11-22|09:18:23] Config overridden from Environment variable logger=settings var="GF_PATHS_LOGS=/var/log/grafana"
INFO [11-22|09:18:23] Config overridden from Environment variable logger=settings var="GF_PATHS_PLUGINS=/var/lib/grafana/plugins"
INFO [11-22|09:18:23] Config overridden from Environment variable logger=settings var="GF_PATHS_PROVISIONING=/etc/grafana/provisioning"
INFO [11-22|09:18:23] Target                                   logger=settings target=[all]
INFO [11-22|09:18:23] Path Home                                logger=settings path=/usr/share/grafana
INFO [11-22|09:18:23] Path Data                                logger=settings path=/var/lib/grafana
INFO [11-22|09:18:23] Path Logs                                logger=settings path=/var/log/grafana
INFO [11-22|09:18:23] Path Plugins                             logger=settings path=/var/lib/grafana/plugins
INFO [11-22|09:18:23] Path Provisioning                        logger=settings path=/etc/grafana/provisioning
INFO [11-22|09:18:23] App mode production                      logger=settings
✔ Downloaded and extracted grafana-lokioperational-app v1.0.6 zip successfully to /var/lib/grafana/plugins/grafana-lokioperational-app

Please restart Grafana after installing or removing plugins. Refer to Grafana documentation for instructions if necessary.
@rsommer

rsommer commented Nov 24, 2025

Copy link
Copy Markdown

@trevorwhitney installing worked out just fine, but are there any ideas on supporting multiple loki clusters in the app? Beforehand I was able to take a peek at the operational view on the clusters itself. Now there is only one app inside grafana which is connectable to only one loki cluster as far as I got until now.

@trevorwhitney

Copy link
Copy Markdown
Collaborator Author

@ondrejmo glad to hear it's working now, looks like there was a delay in pushing to the production channel for the plugin catalog (cc @chewrocca)

@rsommer there is a datasource selector in the plugin, so it should work with all your loki clusters as long as you have a datasource defined for each.

@ispolnitelle

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

Labels

7 participants