## Intro

The Kubernetes Web UI Dashboard is a critical tool for visualizing and managing cluster workloads. In production, however, its operational reliability depends on maintaining the right version, applying safe configuration changes, diagnosing issues proactively, and having clear recovery paths. Without a structured approach, teams may struggle with recurring incidents, configuration drift, or security gaps.

This article provides a practical, example-driven operations checklist for the Kubernetes Dashboard in production. It is designed for developers, DevOps consultants, and startup teams who manage Kubernetes clusters. The checklist covers the following areas:

- Version and environment inventory

- Safe configuration paths

- Verification and diagnostics

- Failure modes and recovery

- Ongoing operations tasks

Each section includes concrete commands, expected outputs, failure signals, and recovery decisions. The goal is operational safety: observe before changing, limit the blast radius, use placeholders instead of secrets, verify results, and document how to recover when expected states are not reached.

## Version and Environment Inventory

Before touching anything, you need to know exactly what version of the Dashboard you are running, its deployment topology, and the surrounding prerequisites. This baseline prevents mismatches between the UI, the API server, and your access controls.

### Identify the Deployed Dashboard Version

Run the following read-only commands:

kubectl get pods -n kubernetes-dashboard -o wide 
 Expected output shows the dashboard pod(s) with their IP, node, and status. For example:

NAME READY STATUS RESTARTS AGE IP NODE
dashboard-metrics-scraper-7c5b7c4b7b-4mz6q 1/1 Running 0 12d 10.244.1.5 node-1
kubernetes-dashboard-5c7b5b4c4d-8q2wz 1/1 Running 0 12d 10.244.2.3 node-2 
 To get the exact image version:

kubectl get deployment kubernetes-dashboard -n kubernetes-dashboard -o jsonpath='{.spec.template.spec.containers[0].image}' 
 Example output:

kubernetesui/dashboard:v2.7.0 
 Check the release notes for that version. Verify it is compatible with your Kubernetes version. For example, Dashboard v2.7.0 is compatible with Kubernetes 1.22-1.26. If you are running Kubernetes 1.28, you must upgrade the Dashboard to v3.0.0 or later.

### Verify Prerequisites

The Dashboard requires:

- A running Kubernetes cluster (version within supported range)

- RBAC enabled (standard in most clusters)

- Network access to the API server from your browser or a proxy

- If using token authentication, a valid service account token

- If using an Ingress, a properly configured ingress controller and TLS certificate

To confirm RBAC is enabled:

kubectl api-versions | grep rbac.authorization.k8s.io 
 Expected output includes rbac.authorization.k8s.io/v1 .

### Document Topology

Create an inventory document with the following concrete details:

<div class="my-stack-md overflow-x-auto">
<table class="min-w-[42rem] border-collapse text-left">
<thead><tr><th scope="col" class="border border-outline-variant bg-surface-container-low px-4 py-3 text-left font-label-md font-semibold text-on-surface">Item</th><th scope="col" class="border border-outline-variant bg-surface-container-low px-4 py-3 text-left font-label-md font-semibold text-on-surface">Value</th></tr></thead>
<tbody><tr><td class="border border-outline-variant px-4 py-3 align-top text-body-md text-on-surface-variant">Dashboard version</td><td class="border border-outline-variant px-4 py-3 align-top text-body-md text-on-surface-variant">v2.7.0</td></tr>
<tr><td class="border border-outline-variant px-4 py-3 align-top text-body-md text-on-surface-variant">Namespace</td><td class="border border-outline-variant px-4 py-3 align-top text-body-md text-on-surface-variant">kubernetes-dashboard</td></tr>
<tr><td class="border border-outline-variant px-4 py-3 align-top text-body-md text-on-surface-variant">Deployment name</td><td class="border border-outline-variant px-4 py-3 align-top text-body-md text-on-surface-variant">kubernetes-dashboard</td></tr>
<tr><td class="border border-outline-variant px-4 py-3 align-top text-body-md text-on-surface-variant">Replicas</td><td class="border border-outline-variant px-4 py-3 align-top text-body-md text-on-surface-variant">1</td></tr>
<tr><td class="border border-outline-variant px-4 py-3 align-top text-body-md text-on-surface-variant">Service type</td><td class="border border-outline-variant px-4 py-3 align-top text-body-md text-on-surface-variant">ClusterIP</td></tr>
<tr><td class="border border-outline-variant px-4 py-3 align-top text-body-md text-on-surface-variant">Ingress/Route</td><td class="border border-outline-variant px-4 py-3 align-top text-body-md text-on-surface-variant">dashboard.example.com (via ingress-nginx)</td></tr>
<tr><td class="border border-outline-variant px-4 py-3 align-top text-body-md text-on-surface-variant">Authentication mode</td><td class="border border-outline-variant px-4 py-3 align-top text-body-md text-on-surface-variant">Token (ServiceAccount: admin-user)</td></tr>
<tr><td class="border border-outline-variant px-4 py-3 align-top text-body-md text-on-surface-variant">Metrics scraper</td><td class="border border-outline-variant px-4 py-3 align-top text-body-md text-on-surface-variant">dashboard-metrics-scraper v1.0.8</td></tr>
<tr><td class="border border-outline-variant px-4 py-3 align-top text-body-md text-on-surface-variant">Certificate issuer</td><td class="border border-outline-variant px-4 py-3 align-top text-body-md text-on-surface-variant">Let&#39;s Encrypt (cert-manager)</td></tr></tbody>
</table>
</div>
This table gives you a clear snapshot. When something changes, update the inventory.

### Practical Check Commands

Use the following sequence to gather comprehensive environment information:

kubectl get all -n kubernetes-dashboard
kubectl describe deployment kubernetes-dashboard -n kubernetes-dashboard
kubectl get events -n kubernetes-dashboard --sort-by=.lastTimestamp | tail -20 
 Look for:

- ImagePullBackOff errors indicating registry issues

- CrashLoopBackOff on the metrics scraper

- FailedScheduling events due to resource constraints

For example, if you see FailedScheduling with Insufficient cpu , you know the node pool needs scaling.

<style>
.eno-quiz-widget{margin:2rem 0;padding:1.5rem;border-radius:12px;background:var(--eno-surface-lowest,#f7f7f8);border:1px solid var(--eno-border-soft,#e2e2e6);font-family:var(--eno-font-body,Inter,sans-serif)}
.eno-quiz-widget .eno-quiz-kicker{font-family:var(--eno-font-label,"JetBrains Mono",monospace);font-size:.75rem;letter-spacing:.05em;text-transform:uppercase;color:var(--eno-text-muted,#5f5f68);margin:0 0 .5rem}
.eno-quiz-widget .eno-quiz-question{font-family:var(--eno-font-heading,"Hanken Grotesk",sans-serif);font-size:1.0625rem;font-weight:600;margin:0 0 1rem;color:var(--eno-text-strong,#1a1a1f)}
.eno-quiz-widget .eno-quiz-options{list-style:none;margin:0;padding:0;display:flex;flex-direction:column;gap:.5rem}
.eno-quiz-widget .eno-quiz-option{display:block;width:100%;min-height:44px;text-align:left;padding:.625rem .875rem;border-radius:8px;border:1.5px solid var(--eno-border-soft,#e2e2e6);background:#fff;font-size:.9375rem;cursor:pointer;transition:border-color var(--eno-motion-base,180ms ease-out),background var(--eno-motion-base,180ms ease-out)}
.eno-quiz-widget .eno-quiz-option:hover{border-color:var(--eno-primary,#0059bb)}
.eno-quiz-widget .eno-quiz-option:focus-visible{outline:none;box-shadow:0 0 0 3px rgb(0 89 187 / 0.15)}
.eno-quiz-widget .eno-quiz-option[aria-pressed="true"]{border-color:var(--eno-primary,#0059bb);background:rgb(0 89 187 / 0.06)}
.eno-quiz-widget .eno-quiz-option[data-correct="true"].eno-quiz-revealed{border-color:var(--eno-success,#17803a);background:rgb(23 128 58 / 0.08)}
.eno-quiz-widget .eno-quiz-option[data-correct="false"].eno-quiz-revealed.eno-quiz-was-selected{border-color:var(--eno-error,#ba1a1a);background:var(--eno-error-soft,#ffdad6)}
.eno-quiz-widget .eno-quiz-option-icon{display:inline-block;width:1.1em;margin-right:.4em;font-weight:700}
.eno-quiz-widget .eno-quiz-submit{margin-top:1rem;min-height:44px;padding:.5rem 1.25rem;border-radius:8px;border:none;background:var(--eno-primary,#0059bb);color:#fff;font-weight:600;font-size:.9375rem;cursor:pointer;transition:opacity var(--eno-motion-base,180ms ease-out)}
.eno-quiz-widget .eno-quiz-submit:disabled{opacity:.5;cursor:not-allowed}
.eno-quiz-widget .eno-quiz-submit:focus-visible{outline:none;box-shadow:0 0 0 3px rgb(0 89 187 / 0.15)}
.eno-quiz-widget .eno-quiz-explanation{margin-top:1rem;padding:.875rem 1rem;border-radius:8px;font-size:.9375rem;line-height:1.5;display:none}
.eno-quiz-widget .eno-quiz-explanation.eno-quiz-visible{display:block}
.eno-quiz-widget .eno-quiz-explanation.eno-quiz-correct{background:rgb(23 128 58 / 0.08);color:var(--eno-success,#17803a)}
.eno-quiz-widget .eno-quiz-explanation.eno-quiz-incorrect{background:var(--eno-error-soft,#ffdad6);color:var(--eno-error,#ba1a1a)}
@media (prefers-reduced-motion: reduce){.eno-quiz-widget *{transition:none!important}}
</style><div class="eno-quiz-widget" role="group" aria-label="Quick check question">
Quick check 1 of 2

What is the recommended method for installing the Kubernetes Dashboard according to the article?

- Using kubectl apply with YAML manifests
- Using Helm chart installation
- Using the Kubernetes web-based UI
- Using kubeadm

<button type="button" class="eno-quiz-submit" disabled>Submit</button>
<div class="eno-quiz-explanation">The article states: &#39;Kubernetes Dashboard supports only Helm-based installation currently as it is faster and gives us better control over all dependencies required by Dashboard to run.&#39;</div>
</div>
<script>
document.addEventListener('DOMContentLoaded', function () {
  document.querySelectorAll('.eno-quiz-widget:not([data-eno-quiz-bound])').forEach(function (widget) {
    widget.setAttribute('data-eno-quiz-bound', '1');
    var options = Array.prototype.slice.call(widget.querySelectorAll('.eno-quiz-option'));
    var submitBtn = widget.querySelector('.eno-quiz-submit');
    var explanation = widget.querySelector('.eno-quiz-explanation');
    var selected = null;
    options.forEach(function (opt) {
      opt.addEventListener('click', function () {
        if (widget.hasAttribute('data-eno-quiz-answered')) return;
        options.forEach(function (o) { o.setAttribute('aria-pressed', 'false'); });
        opt.setAttribute('aria-pressed', 'true');
        selected = opt;
        submitBtn.disabled = false;
      });
    });
    submitBtn.addEventListener('click', function () {
      if (!selected || widget.hasAttribute('data-eno-quiz-answered')) return;
      widget.setAttribute('data-eno-quiz-answered', '1');
      submitBtn.disabled = true;
      var correct = selected.getAttribute('data-correct') === 'true';
      options.forEach(function (o) {
        o.classList.add('eno-quiz-revealed');
        if (o === selected) o.classList.add('eno-quiz-was-selected');
        var icon = o.querySelector('.eno-quiz-option-icon');
        if (o.getAttribute('data-correct') === 'true') icon.textContent = '\u2713';
        else if (o === selected) icon.textContent = '\u2717';
      });
      explanation.classList.add('eno-quiz-visible', correct ? 'eno-quiz-correct' : 'eno-quiz-incorrect');
    });
  });
});
</script>

## Safe Configuration Path

Configuration changes require caution. The principle is: make one change at a time, observe the result, and have a rollback plan. Avoid editing live resources directly when manifests are available.

### Manage Configuration as Code

Store Dashboard configuration (Deployment, Service, RBAC, Ingress) in a Git repository. Use kubectl apply with manifests for reproducibility.

Example: update the Dashboard image version in dashboard-deployment.yaml :

apiVersion: apps/v1
kind: Deployment
metadata:
 name: kubernetes-dashboard
 namespace: kubernetes-dashboard
spec:
 selector:
 matchLabels:
 k8s-app: kubernetes-dashboard
 template:
 metadata:
 labels:
 k8s-app: kubernetes-dashboard
 spec:
 containers:
 - name: kubernetes-dashboard
 image: kubernetesui/dashboard:v2.7.0 # Change to new version
 ports:
 - containerPort: 8443
 protocol: TCP
 args:
 - --auto-generate-certificates
 - --namespace=kubernetes-dashboard 
 Apply with:

kubectl apply -f dashboard-deployment.yaml 
 Verify rollout:

kubectl rollout status deployment/kubernetes-dashboard -n kubernetes-dashboard 
 Expected output:

deployment "kubernetes-dashboard" successfully rolled out 
 If the rollout fails, rollback:

kubectl rollout undo deployment/kubernetes-dashboard -n kubernetes-dashboard 

### Secure Authentication Configuration

 Never use the default self-signed certificate in production. Instead, provide your own TLS certificate via a secret:

kubectl create secret tls kubernetes-dashboard-certs \
 --cert=/path/to/tls.crt \
 --key=/path/to/tls.key \
 -n kubernetes-dashboard 
 Reference it in the Dashboard Deployment args:

args:
 - --tls-cert-file=/certs/tls.crt
 - --tls-key-file=/certs/tls.key
 - --auto-generate-certificates=false 
 Mount the secret as a volume:

volumes:
- name: kubernetes-dashboard-certs
 secret:
 secretName: kubernetes-dashboard-certs 
 And in the container:

volumeMounts:
- name: kubernetes-dashboard-certs
 mountPath: /certs 
 Apply and check the pod restarts with new certs.

### Use a Dedicated Service Account with Least Privilege

Instead of using the default admin privileges, create a service account with read-only access for daily operations:

apiVersion: v1
kind: ServiceAccount
metadata:
 name: dashboard-viewer
 namespace: kubernetes-dashboard 
 Bind it to a read-only ClusterRole (like view ):

apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
 name: dashboard-viewer
roleRef:
 apiGroup: rbac.authorization.k8s.io
 kind: ClusterRole
 name: view
subjects:
- kind: ServiceAccount
 name: dashboard-viewer
 namespace: kubernetes-dashboard 
 Generate a token for this service account for dashboard login (after creating the account and binding):

kubectl create token dashboard-viewer -n kubernetes-dashboard 
 This token expires by default. For long-lived tokens, create a secret manually and extract the token.

### Configuration Verification Checklist

After any configuration change, run:

kubectl get pods -n kubernetes-dashboard
kubectl logs -n kubernetes-dashboard deployment/kubernetes-dashboard --tail=20
curl -I https://dashboard.example.com 
 Check for:

- Pods ready and not restarting

- No TLS certificate errors in logs

- HTTP 200 or redirect from Ingress

## Verification and Diagnostics

Ongoing verification ensures the Dashboard is healthy and responsive. Diagnostics help identify performance bottlenecks, connectivity issues, or resource exhaustion.

### Health Checks

The Dashboard deployment includes liveness and readiness probes by default. Verify they are configured:

kubectl get deployment kubernetes-dashboard -n kubernetes-dashboard -o yaml | grep -A5 -B5 probes 
 Expected snippet:

livenessProbe:
 httpGet:
 path: /
 port: 8443
 scheme: HTTPS
 initialDelaySeconds: 30
 periodSeconds: 10
readinessProbe:
 httpGet:
 path: /
 port: 8443
 scheme: HTTPS
 initialDelaySeconds: 5
 periodSeconds: 10 
 If probes fail, the pod restarts or becomes unready. Use kubectl describe pod to see probe failure events.

### Log Analysis

To quickly identify errors:

kubectl logs -n kubernetes-dashboard deployment/kubernetes-dashboard --tail=50 
 Common errors:

- x509: certificate signed by unknown authority – indicates certificate mismatch

- context deadline exceeded – likely API server unreachable

- 503 Service Unavailable from metrics scraper – check metrics scraper pod

### Network Connectivity Testing

From within the cluster, test the Dashboard service:

kubectl run curl-test -it --rm --image=curlimages/curl -n kubernetes-dashboard -- sh 
 Then inside the pod:

curl -k https://kubernetes-dashboard:443 
 A successful response with HTML content confirms the service works. Test from outside via Ingress:

curl -I https://dashboard.example.com 
 Look for HTTP/2 200 or HTTP/1.1 200 OK .

### Performance Diagnostics

High Dashboard latency can stem from API server load or large resource sets. Monitor API server metrics:

kubectl get --raw /metrics | grep apiserver_request_duration_seconds_sum 
 If Dashboard queries time out, increase the --token-ttl or adjust --insecure-bind-address settings based on your access pattern. But usually, the issue is cluster-wide API load.

### Useful Diagnostic Commands

- kubectl top nodes and kubectl top pods -n kubernetes-dashboard – verify resource usage

- kubectl get events -n kubernetes-dashboard --sort-by=.lastTimestamp – recent events

- kubectl exec -it -n kubernetes-dashboard -- netstat -tulpn – listening ports inside pod

- kubectl port-forward svc/kubernetes-dashboard 8443:443 -n kubernetes-dashboard – local access for testing

 When using port-forward, open https://localhost:8443 in your browser and accept the self-signed certificate if still using one.

<style>
.eno-quiz-widget{margin:2rem 0;padding:1.5rem;border-radius:12px;background:var(--eno-surface-lowest,#f7f7f8);border:1px solid var(--eno-border-soft,#e2e2e6);font-family:var(--eno-font-body,Inter,sans-serif)}
.eno-quiz-widget .eno-quiz-kicker{font-family:var(--eno-font-label,"JetBrains Mono",monospace);font-size:.75rem;letter-spacing:.05em;text-transform:uppercase;color:var(--eno-text-muted,#5f5f68);margin:0 0 .5rem}
.eno-quiz-widget .eno-quiz-question{font-family:var(--eno-font-heading,"Hanken Grotesk",sans-serif);font-size:1.0625rem;font-weight:600;margin:0 0 1rem;color:var(--eno-text-strong,#1a1a1f)}
.eno-quiz-widget .eno-quiz-options{list-style:none;margin:0;padding:0;display:flex;flex-direction:column;gap:.5rem}
.eno-quiz-widget .eno-quiz-option{display:block;width:100%;min-height:44px;text-align:left;padding:.625rem .875rem;border-radius:8px;border:1.5px solid var(--eno-border-soft,#e2e2e6);background:#fff;font-size:.9375rem;cursor:pointer;transition:border-color var(--eno-motion-base,180ms ease-out),background var(--eno-motion-base,180ms ease-out)}
.eno-quiz-widget .eno-quiz-option:hover{border-color:var(--eno-primary,#0059bb)}
.eno-quiz-widget .eno-quiz-option:focus-visible{outline:none;box-shadow:0 0 0 3px rgb(0 89 187 / 0.15)}
.eno-quiz-widget .eno-quiz-option[aria-pressed="true"]{border-color:var(--eno-primary,#0059bb);background:rgb(0 89 187 / 0.06)}
.eno-quiz-widget .eno-quiz-option[data-correct="true"].eno-quiz-revealed{border-color:var(--eno-success,#17803a);background:rgb(23 128 58 / 0.08)}
.eno-quiz-widget .eno-quiz-option[data-correct="false"].eno-quiz-revealed.eno-quiz-was-selected{border-color:var(--eno-error,#ba1a1a);background:var(--eno-error-soft,#ffdad6)}
.eno-quiz-widget .eno-quiz-option-icon{display:inline-block;width:1.1em;margin-right:.4em;font-weight:700}
.eno-quiz-widget .eno-quiz-submit{margin-top:1rem;min-height:44px;padding:.5rem 1.25rem;border-radius:8px;border:none;background:var(--eno-primary,#0059bb);color:#fff;font-weight:600;font-size:.9375rem;cursor:pointer;transition:opacity var(--eno-motion-base,180ms ease-out)}
.eno-quiz-widget .eno-quiz-submit:disabled{opacity:.5;cursor:not-allowed}
.eno-quiz-widget .eno-quiz-submit:focus-visible{outline:none;box-shadow:0 0 0 3px rgb(0 89 187 / 0.15)}
.eno-quiz-widget .eno-quiz-explanation{margin-top:1rem;padding:.875rem 1rem;border-radius:8px;font-size:.9375rem;line-height:1.5;display:none}
.eno-quiz-widget .eno-quiz-explanation.eno-quiz-visible{display:block}
.eno-quiz-widget .eno-quiz-explanation.eno-quiz-correct{background:rgb(23 128 58 / 0.08);color:var(--eno-success,#17803a)}
.eno-quiz-widget .eno-quiz-explanation.eno-quiz-incorrect{background:var(--eno-error-soft,#ffdad6);color:var(--eno-error,#ba1a1a)}
@media (prefers-reduced-motion: reduce){.eno-quiz-widget *{transition:none!important}}
</style><div class="eno-quiz-widget" role="group" aria-label="Quick check question">
Quick check 2 of 2

What command adds the Kubernetes Dashboard Helm repository?

- helm repo add kubernetes-dashboard https://kubernetes.github.io/dashboard/
- kubectl apply -f https://kubernetes.github.io/dashboard/
- helm install dashboard https://kubernetes.github.io/dashboard/
- git clone https://kubernetes.github.io/dashboard/

<button type="button" class="eno-quiz-submit" disabled>Submit</button>
<div class="eno-quiz-explanation">The article command to add the repository is: &#39;helm repo add kubernetes-dashboard https://kubernetes.github.io/dashboard/&#39;.</div>
</div>
<script>
document.addEventListener('DOMContentLoaded', function () {
  document.querySelectorAll('.eno-quiz-widget:not([data-eno-quiz-bound])').forEach(function (widget) {
    widget.setAttribute('data-eno-quiz-bound', '1');
    var options = Array.prototype.slice.call(widget.querySelectorAll('.eno-quiz-option'));
    var submitBtn = widget.querySelector('.eno-quiz-submit');
    var explanation = widget.querySelector('.eno-quiz-explanation');
    var selected = null;
    options.forEach(function (opt) {
      opt.addEventListener('click', function () {
        if (widget.hasAttribute('data-eno-quiz-answered')) return;
        options.forEach(function (o) { o.setAttribute('aria-pressed', 'false'); });
        opt.setAttribute('aria-pressed', 'true');
        selected = opt;
        submitBtn.disabled = false;
      });
    });
    submitBtn.addEventListener('click', function () {
      if (!selected || widget.hasAttribute('data-eno-quiz-answered')) return;
      widget.setAttribute('data-eno-quiz-answered', '1');
      submitBtn.disabled = true;
      var correct = selected.getAttribute('data-correct') === 'true';
      options.forEach(function (o) {
        o.classList.add('eno-quiz-revealed');
        if (o === selected) o.classList.add('eno-quiz-was-selected');
        var icon = o.querySelector('.eno-quiz-option-icon');
        if (o.getAttribute('data-correct') === 'true') icon.textContent = '\u2713';
        else if (o === selected) icon.textContent = '\u2717';
      });
      explanation.classList.add('eno-quiz-visible', correct ? 'eno-quiz-correct' : 'eno-quiz-incorrect');
    });
  });
});
</script>

## Failure Modes and Recovery

Knowing the common failure patterns allows for quicker recovery. Here are specific failure scenarios with detection and recovery steps.

### Scenario 1: Dashboard Pod in CrashLoopBackOff

Detection:

kubectl get pods -n kubernetes-dashboard 
 Output shows RESTARTS increasing and STATUS CrashLoopBackOff .

Investigation:

kubectl describe pod <pod-name> -n kubernetes-dashboard
kubectl logs <pod-name> -n kubernetes-dashboard --previous 
 Possible causes:

- Misconfigured certificate volume

- Missing ConfigMap

- Incompatible arguments

Recovery:

If certificate issue, fix the secret and mount, then delete the pod to force restart:

kubectl delete pod <pod-name> -n kubernetes-dashboard 
 If arguments issue, rollback the deployment to previous version:

kubectl rollout undo deployment/kubernetes-dashboard -n kubernetes-dashboard 

### Scenario 2: Unable to Login to Dashboard

 Detection: Token rejected or browser shows authentication error.

Investigation:

Verify the token belongs to a valid service account:

kubectl get serviceaccount <sa-name> -n kubernetes-dashboard
kubectl get clusterrolebinding <binding-name> -o yaml 
 If using OIDC, check the --oidc-issuer and client settings in the deployment.

Recovery:

Generate a new token:

kubectl create token <sa-name> -n kubernetes-dashboard 
 Or if using a long-lived token secret, extract it:

kubectl get secret <secret-name> -n kubernetes-dashboard -o jsonpath='{.data.token}' | base64 -d 
 Ensure the service account has appropriate RBAC bindings.

### Scenario 3: Dashboard Shows No Data or Metrics

Detection: CPU/memory graphs are empty.

Investigation:

Check metrics scraper pod:

kubectl get pods -n kubernetes-dashboard | grep metrics-scraper
kubectl logs -n kubernetes-dashboard deployment/dashboard-metrics-scraper 
 Common error: dial tcp: lookup kubernetes-dashboard on 10.96.0.10:53: no such host or metrics server not deployed.

Recovery:

Ensure the metrics-server is installed in the cluster:

kubectl get deployment metrics-server -n kube-system 
 If missing, install it. Also verify the scraper service DNS is correct.

### Scenario 4: Dashboard Ingress Returns 502 Bad Gateway

Detection: curl -I https://dashboard.example.com returns 502.

Investigation:

Check Ingress resource and service endpoints:

kubectl get ingress -n kubernetes-dashboard
kubectl get endpoints kubernetes-dashboard -n kubernetes-dashboard 
 If endpoints are empty, the service selector does not match pods. Verify kubectl get pods -n kubernetes-dashboard --show-labels and the service selector.

Recovery:

Fix the service selector or pod labels to match, then check ingress again.

### Recovery Verification Checklist

After any recovery, verify:

- Dashboard pod is Running with no restarts

- Login works with valid token

- Metrics display correctly

- Ingress returns 200 OK

- Logs show no errors in last 5 minutes

## Operations Checklist

Use this summarized checklist as a quick reference for routine and reactive operations. Each item includes the command or action and expected outcome.

<div class="my-stack-md overflow-x-auto">
<table class="min-w-[42rem] border-collapse text-left">
<thead><tr><th scope="col" class="border border-outline-variant bg-surface-container-low px-4 py-3 text-left font-label-md font-semibold text-on-surface">Area</th><th scope="col" class="border border-outline-variant bg-surface-container-low px-4 py-3 text-left font-label-md font-semibold text-on-surface">Item</th><th scope="col" class="border border-outline-variant bg-surface-container-low px-4 py-3 text-left font-label-md font-semibold text-on-surface">Command / Action</th><th scope="col" class="border border-outline-variant bg-surface-container-low px-4 py-3 text-left font-label-md font-semibold text-on-surface">Expected Outcome</th></tr></thead>
<tbody><tr><td class="border border-outline-variant px-4 py-3 align-top text-body-md text-on-surface-variant">Version</td><td class="border border-outline-variant px-4 py-3 align-top text-body-md text-on-surface-variant">Check dashboard version</td><td class="border border-outline-variant px-4 py-3 align-top text-body-md text-on-surface-variant"><code class="font-mono text-[0.9em] bg-surface-container px-1 py-0.5 rounded">kubectl get deployment kubernetes-dashboard -n kubernetes-dashboard -o jsonpath=&#39;{.spec.template.spec.containers[0].image}&#39;</code></td><td class="border border-outline-variant px-4 py-3 align-top text-body-md text-on-surface-variant">Image tag matches supported version</td></tr>
<tr><td class="border border-outline-variant px-4 py-3 align-top text-body-md text-on-surface-variant">Health</td><td class="border border-outline-variant px-4 py-3 align-top text-body-md text-on-surface-variant">Pod status</td><td class="border border-outline-variant px-4 py-3 align-top text-body-md text-on-surface-variant"><code class="font-mono text-[0.9em] bg-surface-container px-1 py-0.5 rounded">kubectl get pods -n kubernetes-dashboard</code></td><td class="border border-outline-variant px-4 py-3 align-top text-body-md text-on-surface-variant">All pods Running, READY 1/1</td></tr>
<tr><td class="border border-outline-variant px-4 py-3 align-top text-body-md text-on-surface-variant">Health</td><td class="border border-outline-variant px-4 py-3 align-top text-body-md text-on-surface-variant">Check pod events</td><td class="border border-outline-variant px-4 py-3 align-top text-body-md text-on-surface-variant"><code class="font-mono text-[0.9em] bg-surface-container px-1 py-0.5 rounded">kubectl describe pod &lt;pod-name&gt; -n kubernetes-dashboard</code></td><td class="border border-outline-variant px-4 py-3 align-top text-body-md text-on-surface-variant">No warning or error events</td></tr>
<tr><td class="border border-outline-variant px-4 py-3 align-top text-body-md text-on-surface-variant">Logs</td><td class="border border-outline-variant px-4 py-3 align-top text-body-md text-on-surface-variant">Recent errors</td><td class="border border-outline-variant px-4 py-3 align-top text-body-md text-on-surface-variant"><code class="font-mono text-[0.9em] bg-surface-container px-1 py-0.5 rounded">kubectl logs -n kubernetes-dashboard deployment/kubernetes-dashboard --tail=20</code></td><td class="border border-outline-variant px-4 py-3 align-top text-body-md text-on-surface-variant">No error stack traces</td></tr>
<tr><td class="border border-outline-variant px-4 py-3 align-top text-body-md text-on-surface-variant">Config</td><td class="border border-outline-variant px-4 py-3 align-top text-body-md text-on-surface-variant">TLS certificate expiry</td><td class="border border-outline-variant px-4 py-3 align-top text-body-md text-on-surface-variant"><code class="font-mono text-[0.9em] bg-surface-container px-1 py-0.5 rounded">kubectl get secret kubernetes-dashboard-certs -n kubernetes-dashboard -o jsonpath=&#39;{.data.tls\.crt}&#39; | base64 -d | openssl x509 -noout -dates</code></td><td class="border border-outline-variant px-4 py-3 align-top text-body-md text-on-surface-variant">Not expired; renew if &lt;30 days</td></tr>
<tr><td class="border border-outline-variant px-4 py-3 align-top text-body-md text-on-surface-variant">Security</td><td class="border border-outline-variant px-4 py-3 align-top text-body-md text-on-surface-variant">Service account tokens</td><td class="border border-outline-variant px-4 py-3 align-top text-body-md text-on-surface-variant"><code class="font-mono text-[0.9em] bg-surface-container px-1 py-0.5 rounded">kubectl get secrets -n kubernetes-dashboard</code></td><td class="border border-outline-variant px-4 py-3 align-top text-body-md text-on-surface-variant">Only expected secrets; no leaked tokens in logs</td></tr>
<tr><td class="border border-outline-variant px-4 py-3 align-top text-body-md text-on-surface-variant">Network</td><td class="border border-outline-variant px-4 py-3 align-top text-body-md text-on-surface-variant">Ingress connectivity</td><td class="border border-outline-variant px-4 py-3 align-top text-body-md text-on-surface-variant"><code class="font-mono text-[0.9em] bg-surface-container px-1 py-0.5 rounded">curl -I https://dashboard.example.com</code></td><td class="border border-outline-variant px-4 py-3 align-top text-body-md text-on-surface-variant">HTTP 200</td></tr>
<tr><td class="border border-outline-variant px-4 py-3 align-top text-body-md text-on-surface-variant">Metrics</td><td class="border border-outline-variant px-4 py-3 align-top text-body-md text-on-surface-variant">Scraper health</td><td class="border border-outline-variant px-4 py-3 align-top text-body-md text-on-surface-variant"><code class="font-mono text-[0.9em] bg-surface-container px-1 py-0.5 rounded">kubectl get pods -n kubernetes-dashboard | grep metrics-scraper</code></td><td class="border border-outline-variant px-4 py-3 align-top text-body-md text-on-surface-variant">Running</td></tr>
<tr><td class="border border-outline-variant px-4 py-3 align-top text-body-md text-on-surface-variant">Backup</td><td class="border border-outline-variant px-4 py-3 align-top text-body-md text-on-surface-variant">Export Dashboard settings</td><td class="border border-outline-variant px-4 py-3 align-top text-body-md text-on-surface-variant"><code class="font-mono text-[0.9em] bg-surface-container px-1 py-0.5 rounded">kubectl get deployment,svc,ingress,cm,secret -n kubernetes-dashboard -o yaml &gt; dashboard-backup.yaml</code></td><td class="border border-outline-variant px-4 py-3 align-top text-body-md text-on-surface-variant">Backup file created</td></tr>
<tr><td class="border border-outline-variant px-4 py-3 align-top text-body-md text-on-surface-variant">Recovery</td><td class="border border-outline-variant px-4 py-3 align-top text-body-md text-on-surface-variant">Rollback procedure</td><td class="border border-outline-variant px-4 py-3 align-top text-body-md text-on-surface-variant"><code class="font-mono text-[0.9em] bg-surface-container px-1 py-0.5 rounded">kubectl rollout undo deployment/kubernetes-dashboard -n kubernetes-dashboard</code></td><td class="border border-outline-variant px-4 py-3 align-top text-body-md text-on-surface-variant">Previous version restored</td></tr></tbody>
</table>
</div>
This checklist should be integrated into your team's runbooks and automated where possible.

## Conclusion

A production-ready Kubernetes Web UI Dashboard requires more than a successful deployment. It demands a disciplined approach to version management, configuration control, diagnostics, and failure recovery. The checklist and examples in this article provide a foundation for building your own operational runbook.

The key principles remain:

- Observe before changing: always gather current state and logs.

- Limit blast radius: change one variable at a time.

- Protect secrets: avoid logging tokens; use placeholders.

- Verify outcomes: use explicit commands with expected outputs.

- Document recovery: know how to roll back before you need to.

As a next step, choose one low-risk verification from the article, such as checking Dashboard version compatibility. Record the current state, run the command, and compare the result with the expected output. Then proceed to review your authentication and certificate setup. By integrating these practices into your regular operations, you will reduce downtime and improve the reliability of your Kubernetes Dashboard in production.