Skip to main content
  1. Posts/

Building a Self-Updating Dashboard with Homepage and Kubernetes

·539 words·3 mins·

One of the challenges of running a homelab with dozens of services is keeping track of what’s running and where. I recently deployed Homepage - a modern, fully static dashboard that automatically discovers services from Kubernetes ingresses.

Why Homepage?
#

I evaluated several dashboard options including Homarr and Heimdall. Homepage stood out for a few reasons:

  • Kubernetes-native service discovery - no manual configuration needed
  • Real-time pod status - shows if services are actually running
  • Clean, modern UI - dark theme, customizable layout
  • Lightweight - just a static site with no database

The Setup
#

Homepage runs as a simple deployment in Kubernetes with a ServiceAccount that has read access to the cluster. The magic happens through ingress annotations.

Base Deployment
#

apiVersion: apps/v1
kind: Deployment
metadata:
  name: homepage
spec:
  replicas: 1
  template:
    spec:
      serviceAccountName: homepage
      initContainers:
        - name: copy-config
          image: busybox:1.36
          command: ['sh', '-c', 'cp /config-source/* /config/']
          volumeMounts:
            - name: config-source
              mountPath: /config-source
            - name: config
              mountPath: /config
      containers:
        - name: homepage
          image: ghcr.io/gethomepage/homepage:v0.10.9
          volumeMounts:
            - name: config
              mountPath: /app/config
      volumes:
        - name: config-source
          configMap:
            name: homepage-config
        - name: config
          emptyDir: {}

The init container pattern is important here - Homepage needs a writable config directory to create log files, but ConfigMaps are read-only. We copy the config to an emptyDir volume at startup.

Service Discovery via Annotations
#

The real power comes from ingress annotations. For each service you want on the dashboard, add these annotations:

apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: frigate
  annotations:
    gethomepage.dev/enabled: "true"
    gethomepage.dev/name: "Frigate"
    gethomepage.dev/group: "Home Automation"
    gethomepage.dev/icon: "frigate.png"
    gethomepage.dev/pod-selector: "app=frigate"

The pod-selector annotation is crucial for status monitoring. Homepage uses this to find the actual pods and show whether they’re running. Without it, you’ll see “not found” errors.

ConfigMap for Layout
#

The ConfigMap controls the dashboard layout and widgets:

apiVersion: v1
kind: ConfigMap
metadata:
  name: homepage-config
data:
  settings.yaml: |
    title: Homelab
    theme: dark
    color: slate
    headerStyle: clean
    layout:
      Home Automation:
        style: row
        columns: 4
      Media:
        style: row
        columns: 4
      Infrastructure:
        style: row
        columns: 4

  widgets.yaml: |
    - kubernetes:
        cluster:
          show: true
          cpu: true
          memory: true
          showLabel: true
          label: "cluster"
        nodes:
          show: true
          cpu: true
          memory: true

  kubernetes.yaml: |
    mode: cluster

  services.yaml: |
    []

  docker.yaml: |

Note that services.yaml and docker.yaml must be valid YAML (even if empty). Setting services.yaml: "[]" prevents JavaScript errors from empty files.

Gotchas I Encountered
#

1. Empty Config Files Cause JS Errors
#

If services.yaml or docker.yaml are empty or contain only comments, Homepage throws a classList null reference error. Always set them to valid YAML.

2. Pod Selector Mismatches
#

Homepage defaults to looking for pods with app.kubernetes.io/name=<ingress-name>. Most of my deployments use simpler labels like app=frigate. The gethomepage.dev/pod-selector annotation fixes this.

3. Icon Names
#

Icons come from the Dashboard Icons project. Some names aren’t obvious:

  • ArgoCD: argo-cd.png (not argocd.png)
  • Z-Wave JS: mdi-z-wave (Material Design Icons format)

Result
#

Now every time I add a new service with the proper annotations, it automatically appears on my dashboard with real-time status. No more manually updating bookmark pages or forgetting what port something runs on.

The dashboard shows CPU/memory usage for the cluster and nodes, groups services logically, and immediately tells me if something is down. It’s become my starting point for accessing everything in the homelab.