Homepage

One pane of glass for every service in the lab

Homepage renders a single dashboard linking every service, with live status widgets where a service exposes them. Its configuration is declarative YAML in Git, which is why it is one of the few auto-synced entries.

ArgoCD configuration

Excerpt from argocd-apps/values.yaml in the argocd-apps chart, with annotations added for this site:

homepage:
  application: true
  project: cluster-services
  # true: dashboard YAML is declarative and risk-free to auto-apply
  autoSync: true

Chart values

The full homepage/values.yaml from the service's own chart:

ports:
  http: 3000

image:
  name: registry.opi5cluster.co.uk/homepage:v2.0

volumeMounts:
  mountPath: /app/config/

resources:
  requests:
    cpu: 15m
    memory: 184M

config:
  settings:
    layout:
      - Media:
          style: row
          columns: 3
      - Home Lab:
          style: row
          columns: 3
      - External Apps:
          style: column
          columns: 1
      - Home Admin:
          style: column
          columns: 1
      - Home Utilities:
          style: column
          columns: 1

  bookmarks: []

  services:
    - Media:
        - Seerr:
            icon: seerr
            href: https://seerr.opi5cluster.co.uk/
            description: Media Discovery Service
        - Jellyfin:
            icon: jellyfin
            href: https://jellyfin.opi5cluster.co.uk/
            description: Media Centre
        - qBittorrent:
            icon: qbittorrent
            href: https://qbittorrent.opi5cluster.co.uk/
            description: Torrent Manager
        - Sonarr:
            icon: sonarr
            href: https://sonarr.opi5cluster.co.uk/
            description: Series Management
        - Radarr:
            icon: radarr
            href: https://radarr.opi5cluster.co.uk/
            description: Movie Management
        - Prowlarr:
            icon: prowlarr
            href: https://prowlarr.opi5cluster.co.uk/
            description: Series Management
        - Bazarr:
            icon: bazarr
            href: https://bazarr.opi5cluster.co.uk/
            description: Subtitles Management
        - Tdarr:
            icon: tdarr
            href: https://tdarr.opi5cluster.co.uk/
            description: Transcode Automation

    - Home Lab:
        - ArgoCD:
            icon: /icons/argocd.svg
            href: https://argocd.opi5cluster.co.uk
            description: Declarative GitOps CD for Kubernetes
        - Argo Workflows:
            icon: /icons/argocd.svg
            href: https://argo-workflows.opi5cluster.co.uk
            description: Workflow Engine for Kubernetes
        - Argo Rolleouts:
            icon: /icons/argocd.svg
            href: https://argo-rollouts.opi5cluster.co.uk
            description: Progressive Delivery for Kubernetes
        - Istio (Kiali):
            icon: istio
            href: https://kiali.opi5cluster.co.uk
            description: Reverse Proxy
        - Longhorn:
            icon: /icons/longhorn.svg
            href: https://longhorn.opi5cluster.co.uk
            description: Distributed Block Storage
        - RustFS:
            icon: rustfs
            href: https://rustfs.opi5cluster.co.uk
            description: Object Storage
        - RustFS Backup:
            icon: rustfs
            href: http://backup-raspi3.opi5cluster.co.uk:9001/
            description: Object Storage - Backup
        - AutoMQ:
            icon: kafka-dark
            href: https://automq.opi5cluster.co.uk
            description: Kafka-Compatible Event Streaming
        - Grafana:
            icon: /icons/grafana.svg
            href: https://grafana.opi5cluster.co.uk
            description: Observability
        - PG Admin:
            icon: pgadmin
            href: https://pgadmin.opi5cluster.co.uk/browser/
            description: PostgreSQL Management Tool
        - OpenWebUi:
            icon: open-webui
            href: https://open-webui.opi5cluster.co.uk
            description: Self-hosted Generative AI Chatbot
        - Vault:
            icon: vault
            href: https://vault.opi5cluster.co.uk
            description: Secrets Management
        - Zot:
            icon: zot-registry
            href: https://registry.opi5cluster.co.uk/explore
            description: Container Registry
        - Velero UI:
            icon: velero
            href: https://velero.opi5cluster.co.uk
            description: K3s Backup Orchestration System

    - External Apps:
        - Gitlab:
            icon: gitlab
            href: https://gitlab.com/opi5-cluster
            description: Source Control and CI
        - Cloudflare:
            icon: cloudflare
            href: https://dash.cloudflare.com/
            description: Connectivity and Content Cloud Provider
        - Docker Hub:
            icon: docker-moby
            href: https://hub.docker.com/
            description: Container Registry
        - aKeyless:
            icon: valkey
            href: https://console.akeyless.io/items
            description: Secrets Management
        - ChatGPT:
            icon: chatgpt
            href: https://chatgpt.com/
            description: Generative AI Chatbot
        - Deepseek:
            icon: deepseek
            href: https://chat.deepseek.com/
            description: Generative AI Chatbot
        - Claude:
            icon: claude-ai
            href: https://claude.ai/chat
            description: Generative AI Chatbot
        - Perplexity:
            icon: perplexity
            href: https://www.perplexity.ai/
            description: Generative AI Chatbot

    - Home Admin:
        - Glinet Router:
            icon: /icons/glinet.svg
            href: http://glinet-router.opi5cluster.co.uk
            description: Router Glinet
        - AdGuardHome(DNS01):
            icon: adguard-home
            href: http://dns01.opi5cluster.co.uk/
            description: Ad blocking and privacy protection software
        - AdGuardHome(DNS02):
            icon: adguard-home
            href: http://dns02.opi5cluster.co.uk/
            description: Ad blocking and privacy protection software
        - Beacon:
            icon: /icons/beacon.svg
            href: https://beacon.blocksensus.app
            description: Discord Notifications and Events
        - WoS-Assistant:
            icon: /icons/wos-assistant.svg
            href: https://wos-assistant.blocksensus.app
            description: WoS Assistant

    - Home Utilities:
        - Bracknell Council:
            icon: /icons/council.svg
            href: https://www.bracknell-forest.gov.uk/
            description: Bracknell Council
        - Octopus Energy:
            icon: /icons/octopus-energy.svg
            href: https://octopus.energy/dashboard/new/accounts/A-9A8FCFB4/dashboard
            description: Energy Provider
        - Hyperoptic:
            icon: /icons/hyperoptic-broadband.svg
            href: https://hyperoptic.com/myaccount-login/
            description: Broadband Provider

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

  kubernetes:
    mode: cluster

Manifests & templates

templates/configmap.yaml

ConfigMap holding non-sensitive application configuration.

Show manifest
apiVersion: v1
kind: ConfigMap
metadata:
  name: {{ .Release.Namespace }}-configmap
  namespace: {{ .Release.Namespace }}
data:
  bookmarks.yaml: {{- if .Values.config.bookmarks }} |
{{- .Values.config.bookmarks | toYaml | nindent 4}}
{{- else }} ""
{{- end }}
  docker.yaml: {{- if .Values.config.docker }} |
{{- .Values.config.docker | toYaml | nindent 4 }}
{{- else }} ""
{{- end }}
  kubernetes.yaml: {{- if .Values.config.kubernetes }} |
{{- .Values.config.kubernetes | toYaml | nindent 4 }}
{{- else }} ""
{{- end }}
  services.yaml: {{- if .Values.config.services }} |
{{- .Values.config.services | toYaml | nindent 4 }}
{{- else }} ""
{{- end }}
  settings.yaml: {{- if .Values.config.settings }} |
{{- .Values.config.settings | toYaml | nindent 4 }}
{{- else }} ""
{{- end }}
  widgets.yaml: {{- if .Values.config.widgets }} |
{{- .Values.config.widgets | toYaml | nindent 4 }}
{{- else }} ""
{{- end }}

templates/http-route.yaml

Gateway API HTTPRoute exposing the service through the Istio gateway under opi5cluster.co.uk.

Show manifest
apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
  name: {{ .Release.Namespace }}-httproute
  namespace: {{ .Release.Namespace }}
  annotations:
    link.argocd.argoproj.io/external-link: "https://{{ .Release.Namespace }}.opi5cluster.co.uk"
spec:
  parentRefs:
    - name: istio-gateway
      namespace: istio
      sectionName: websecure
  hostnames:
    - {{ .Release.Namespace }}.opi5cluster.co.uk
  rules:
    - backendRefs:
        - name: {{ .Release.Namespace }}-service
          port: {{ .Values.ports.http }}

templates/rbac.yaml

RBAC letting the Homepage service account read cluster state for its service widgets.

Show manifest
---
apiVersion: v1
kind: Secret
type: kubernetes.io/service-account-token
metadata:
  name: {{ .Release.Namespace }}-service-account-token
  namespace: {{ .Release.Namespace }}
  annotations:
    kubernetes.io/service-account.name: {{ .Release.Namespace }}-service-account
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
  name: {{ .Release.Namespace }}-cluster-role
  namespace: {{ .Release.Namespace }}
rules:
  - apiGroups:
      - ""
    resources:
      - namespaces
      - pods
      - nodes
    verbs:
      - get
      - list
  - apiGroups:
      - extensions
      - networking.k8s.io
    resources:
      - ingresses
    verbs:
      - get
      - list
  - apiGroups:
      - gateway.networking.k8s.io
    resources:
      - httproutes
    verbs:
      - get
      - list
  - apiGroups:
      - metrics.k8s.io
    resources:
      - nodes
      - pods
    verbs:
      - get
      - list
  - apiGroups:
      - apiextensions.k8s.io
    resources:
      - customresourcedefinitions/status
    verbs:
      - get
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
  name: {{ .Release.Namespace }}-cluster-binding
  namespace: {{ .Release.Namespace }}
roleRef:
  apiGroup: rbac.authorization.k8s.io
  kind: ClusterRole
  name: {{ .Release.Namespace }}-cluster-role
subjects:
  - kind: ServiceAccount
    name: {{ .Release.Namespace }}-service-account
    namespace: {{ .Release.Namespace }}

templates/rollout.yaml

Argo Rollout workload: container spec, probes, resources and rollout strategy.

Show manifest
apiVersion: argoproj.io/v1alpha1
kind: Rollout
metadata:
  name: {{ .Release.Namespace }}-rollout
  namespace: {{ .Release.Namespace }}
  labels:
    app.kubernetes.io/name: {{ .Release.Namespace }}
  annotations:
    gethomepage.dev/description: Dynamically Detected Homepage
    gethomepage.dev/enabled: "true"
    gethomepage.dev/group: Cluster Management
    gethomepage.dev/icon: homepage.png
    gethomepage.dev/name: Homepage
spec:
  replicas: 1
  strategy:
    canary:
      steps:
      - setWeight: 25
      - pause: {duration: 30s}
      - setWeight: 50
      - pause: {duration: 45s}
      - setWeight: 100
  selector:
    matchLabels:
      app: {{ .Release.Namespace }}
  template:
    metadata:
      labels:
        app: {{ .Release.Namespace }}
    spec:
      serviceAccount: {{ .Release.Namespace }}-service-account
      serviceAccountName: {{ .Release.Namespace }}-service-account
      automountServiceAccountToken: true
      #imagePullSecrets:
      #  - name: {{ .Values.image.imagePullSecretsName }}
      containers:
      - name: {{ .Release.Namespace }}
        image: {{ .Values.image.name  }}
        imagePullPolicy: IfNotPresent
        securityContext:
          runAsUser: 2000
          runAsGroup: 2000
        env:
          - name: HOMEPAGE_ALLOWED_HOSTS
            value: "{{ .Release.Namespace }}.opi5cluster.co.uk"
        ports:
        - name: {{ .Release.Namespace }}-http
          containerPort: {{ .Values.ports.http }}
          protocol: TCP
        volumeMounts:
        - name: {{ .Release.Namespace }}-config
          mountPath: {{ .Values.volumeMounts.mountPath }}/settings.yaml
          subPath: settings.yaml
        - name: {{ .Release.Namespace }}-config
          mountPath: {{ .Values.volumeMounts.mountPath }}/services.yaml
          subPath: services.yaml
        - name: {{ .Release.Namespace }}-config
          mountPath: {{ .Values.volumeMounts.mountPath }}/widgets.yaml
          subPath: widgets.yaml
        - name: {{ .Release.Namespace }}-config
          mountPath: {{ .Values.volumeMounts.mountPath }}/bookmarks.yaml
          subPath: bookmarks.yaml
        - name: {{ .Release.Namespace }}-config
          mountPath: {{ .Values.volumeMounts.mountPath }}/kubernetes.yaml
          subPath: kubernetes.yaml
        - name: {{ .Release.Namespace }}-config
          mountPath: {{ .Values.volumeMounts.mountPath }}/docker.yaml
          subPath: docker.yaml
        {{- with .Values.resources }}
        resources:
          {{- . | toYaml | nindent 10 }}
        {{- end }}
      dnsPolicy: ClusterFirst
      restartPolicy: Always
      schedulerName: default-scheduler
      volumes:
      - name: {{ .Release.Namespace }}-config
        configMap:
          name: {{ .Release.Namespace }}-configmap

templates/service-account.yaml

ServiceAccount the workload runs as.

Show manifest
apiVersion: v1
kind: ServiceAccount
metadata:
  name: {{ .Release.Namespace }}-service-account
  namespace: {{ .Release.Namespace }}

templates/service.yaml

ClusterIP Service fronting the workload.

Show manifest
apiVersion: v1
kind: Service
metadata:
  name: {{ .Release.Namespace }}-service
  namespace: {{ .Release.Namespace }}
spec:
  type: ClusterIP
  selector:
    app: {{ .Release.Namespace }}
  ipFamilyPolicy: SingleStack
  ports:
    - name: {{ .Release.Namespace }}-http
      protocol: TCP
      port: {{ .Values.ports.http }}
      targetPort: {{ .Values.ports.http }}

Trade-offs

Decision. autoSync on

Alternative. Manual sync

Why. Dashboard config is pure YAML with no runtime state; auto-applying keeps the pane accurate after every Git change with zero risk.

← Back to Observability & Dashboard · All service groups