Skip to content

Homepage Dashboard

Overview

Homepage is a modern, fully static, fast, secure fully proxied, highly customizable application dashboard with integrations for over 100 services and translations into multiple languages. It provides a single pane of glass for accessing and monitoring all homelab services.

Deployment Details:

  • Namespace: homepage
  • Chart: jameswynn/homepage v2.x.x
  • Image: ghcr.io/gethomepage/homepage:v1.8.0
  • URL: https://home.skaggsfamily.us
  • Repository: flux-repo/apps/_bases/homepage and flux-repo/apps/overlays/prod/homepage

Features

  • Kubernetes Integration: Real-time cluster metrics via metrics-server
  • Service Widgets: API-driven widgets for monitoring services (Sonarr, Radarr, etc.)
  • Single Sign-On: Protected by Authelia for secure access
  • Responsive Design: Works on desktop, tablet, and mobile
  • Customizable: Bookmarks, services, and widgets configured via YAML

Architecture

Components

  • Homepage Pod: Main dashboard application
  • ServiceAccount: RBAC for Kubernetes API access (cluster metrics)
  • ConfigMap: Service and widget configurations (rendered from HelmRelease values)
  • SealedSecrets: API keys for service widget integrations
  • IngressRoute: Traefik routing with HTTPS and Authelia middleware

Data Flow

User → Traefik (HTTPS) → Authelia (SSO) → Homepage Pod
                                    ┌─────────┴──────────┐
                                    ↓                    ↓
                            Kubernetes API      Service APIs (via secrets)
                            (cluster metrics)   (Sonarr, Radarr, etc.)

Adding Service Widgets

Service widgets display real-time data from applications (e.g., Sonarr shows wanted/queued series). Most widgets require API keys for authentication.

Pattern: Individual SealedSecrets per Service

To avoid keeping plaintext API keys around, each service gets its own SealedSecret that can be created independently.

Step-by-Step: Adding a New Service Widget

1. Get the API Key

Find the API key in your service's settings (usually under Settings → General or Settings → API).

Example: Sonarr API key is found at Settings → General → Security → API Key

2. Create a SealedSecret

# Set your kubeconfig
export KUBECONFIG=/Users/dskaggs/Projects/homelab/infra-talos/terraform/talos/configs/kubeconfig

# Create the SealedSecret (replace SERVICE_NAME and API_KEY)
# IMPORTANT: The secret key MUST include the HOMEPAGE_VAR_ prefix
kubectl create secret generic homepage-SERVICE_NAME-api \
  --from-literal=HOMEPAGE_VAR_SERVICE_NAME_API_KEY=YOUR_API_KEY \
  --namespace=homepage \
  --dry-run=client -o yaml | \
  kubeseal --cert /Users/dskaggs/.ssh/k8s.crt --format yaml > \
  /Users/dskaggs/Projects/homelab/flux-repo/apps/overlays/prod/homepage/sealedsecret-SERVICE_NAME.yaml

# Example for Sonarr:
kubectl create secret generic homepage-sonarr-api \
  --from-literal=HOMEPAGE_VAR_SONARR_API_KEY=6133eacc718e442593fb806045485ca4 \
  --namespace=homepage \
  --dry-run=client -o yaml | \
  kubeseal --cert /Users/dskaggs/.ssh/k8s.crt --format yaml > \
  /Users/dskaggs/Projects/homelab/flux-repo/apps/overlays/prod/homepage/sealedsecret-sonarr.yaml

Result: Creates sealedsecret-SERVICE_NAME.yaml with encrypted API key

3. Add SealedSecret to Production Overlay Kustomization

Edit flux-repo/apps/overlays/prod/homepage/kustomization.yaml:

resources:
  - ../../../_bases/homepage
  - sealedsecret-sonarr.yaml    # Add this line

4. Mount Secret in Homepage HelmRelease

Edit flux-repo/apps/_bases/homepage/helmrelease.yaml:

# Mount API key secrets as environment variables
envFrom:
  - secretRef:
      name: homepage-sonarr-api
      optional: true
  # Add new services here:
  # - secretRef:
  #     name: homepage-radarr-api
  #     optional: true

Note: The optional: true prevents Homepage from failing if a secret doesn't exist yet.

5. Configure the Service Widget

Edit flux-repo/apps/overlays/prod/homepage/kustomization.yaml in the services patch section:

- Media:
    - Sonarr:
        href: https://sonarr.skaggsfamily.us
        description: TV Series Management
        icon: sonarr.png
        widget:
          type: sonarr
          url: http://sonarr.autopirate:80    # Cluster-internal service URL
          key: "{{HOMEPAGE_VAR_SONARR_API_KEY}}"  # Template variable

Important:

  • URL: Use the cluster-internal service URL (format: http://SERVICE_NAME.NAMESPACE:PORT)
  • Key: Use Homepage's template syntax: {{HOMEPAGE_VAR_SECRET_KEY_NAME}}
  • Service port: Verify the correct port with kubectl get svc -n NAMESPACE

6. Validate and Deploy

# Validate Kustomize build
cd /Users/dskaggs/Projects/homelab/flux-repo
kustomize build apps/overlays/prod/homepage > /dev/null && echo "Build successful"

# Commit changes
git add apps/_bases/homepage/helmrelease.yaml \
        apps/overlays/prod/homepage/kustomization.yaml \
        apps/overlays/prod/homepage/sealedsecret-SERVICE_NAME.yaml

git commit -m "Add SERVICE_NAME widget to Homepage"
git push

# Monitor deployment
export KUBECONFIG=/Users/dskaggs/Projects/homelab/infra-talos/terraform/talos/configs/kubeconfig
kubectl get pods -n homepage -w

Flux will automatically reconcile the changes within 1-2 minutes.

Widget Configuration Reference

Finding Widget Documentation

Homepage widget documentation: https://gethomepage.dev/widgets/services/

Each widget has specific requirements. Common patterns:

  • type: Widget type (e.g., sonarr, radarr, prowlarr)
  • url: Service URL (cluster-internal recommended)
  • key: API key (use template variables)
  • Optional fields: Vary by widget (check documentation)

Common Service URLs and Ports

Service Namespace Service Name Port Widget URL
Sonarr autopirate sonarr 80 http://sonarr.autopirate:80
Radarr autopirate radarr 80 http://radarr.autopirate:80
Prowlarr autopirate prowlarr 80 http://prowlarr.autopirate:80
SABnzbd autopirate sabnzbd 80 http://sabnzbd.autopirate:80
Traefik traefik traefik 9000 http://traefik.traefik:9000

Tip: Verify port with kubectl get svc SERVICE_NAME -n NAMESPACE

Environment Variable Naming

CRITICAL: The environment variable name in your secret must match the template variable exactly, including the HOMEPAGE_VAR_ prefix.

# Secret MUST contain (with HOMEPAGE_VAR_ prefix):
HOMEPAGE_VAR_SONARR_API_KEY: abc123

# Template references as:
{{HOMEPAGE_VAR_SONARR_API_KEY}}

# ❌ WRONG - This will NOT work:
SONARR_API_KEY: abc123  # Missing HOMEPAGE_VAR_ prefix

Why this matters: Homepage does NOT automatically add the prefix. The template variable {{HOMEPAGE_VAR_SONARR_API_KEY}} looks for an environment variable with that exact name. If your secret key is SONARR_API_KEY, Homepage won't find it and you'll get 401 Unauthorized errors.

Configuration Files

Base Configuration

Location: flux-repo/apps/_bases/homepage/

  • helmrelease.yaml: Helm chart deployment with default values
  • rbac.yaml: ServiceAccount and ClusterRole for Kubernetes API access
  • ingressroute.yaml: Traefik HTTP and HTTPS routes
  • certificate.yaml: TLS certificate (cert-manager)
  • dnsendpoint.yaml: DNS record (ExternalDNS)
  • kustomization.yaml: Lists all base resources

Production Overlay

Location: flux-repo/apps/overlays/prod/homepage/

  • kustomization.yaml: Patches for production environment (domains, services, widgets)
  • sealedsecret-*.yaml: Individual SealedSecrets for service API keys

Key Configuration Sections

Kubernetes Widget

Displays cluster metrics (CPU, memory, node count):

widgets:
  - kubernetes:
      cluster:
        show: true
        cpu: true
        memory: true
        showLabel: true
        label: "Production Cluster"
      nodes:
        show: false    # Required in v1.8.0+

Service Widget Example (Sonarr)

services:
  - Media:
      - Sonarr:
          href: https://sonarr.skaggsfamily.us
          description: TV Series Management
          icon: sonarr.png
          widget:
            type: sonarr
            url: http://sonarr.autopirate:80
            key: "{{HOMEPAGE_VAR_SONARR_API_KEY}}"

Troubleshooting

Widget Shows "API Error: Unknown error"

Symptoms: Widget displays error with ECONNREFUSED or connection timeout

Common Causes:

  1. Wrong port: Verify service port with kubectl get svc -n NAMESPACE
  2. Service not running: Check pod status with kubectl get pods -n NAMESPACE
  3. Wrong namespace: Ensure service URL matches actual namespace
  4. API key incorrect: Verify API key in service settings

Fix:

# Check service details
kubectl get svc SERVICE_NAME -n NAMESPACE

# Update widget URL to match service port
# Edit apps/overlays/prod/homepage/kustomization.yaml
# Commit and push changes

Widget Shows "API Error: Unauthorized" (401 Error)

Symptoms: Widget displays 401 Unauthorized error, browser console shows errors like:

GET https://home.skaggsfamily.us/api/services/proxy?group=Media&service=Sonarr&index=0&endpoint=queue 401

Most Common Cause: Secret key missing HOMEPAGE_VAR_ prefix

The environment variable name in your secret must match the template variable exactly: * ✅ Correct: HOMEPAGE_VAR_SONARR_API_KEY=abc123 * ❌ Wrong: SONARR_API_KEY=abc123

Fix:

# 1. Delete the old secret
kubectl delete secret homepage-SERVICE-api -n homepage

# 2. Recreate SealedSecret with correct naming (note HOMEPAGE_VAR_ prefix)
kubectl create secret generic homepage-sonarr-api \
  --from-literal=HOMEPAGE_VAR_SONARR_API_KEY=YOUR_API_KEY \
  --namespace=homepage \
  --dry-run=client -o yaml | \
  kubeseal --cert /Users/dskaggs/.ssh/k8s.crt --format yaml > \
  apps/overlays/prod/homepage/sealedsecret-sonarr.yaml

# 3. Commit and push
git add apps/overlays/prod/homepage/sealedsecret-sonarr.yaml
git commit -m "Fix SERVICE API key environment variable name"
git push

# 4. Restart Homepage
kubectl rollout restart deployment homepage -n homepage

Verify:

# Check environment variable is correct (should show HOMEPAGE_VAR_ prefix)
kubectl exec -n homepage deployment/homepage -- env | grep SERVICE

Other possible causes: 1. API key is incorrect - verify in service settings 2. Service API endpoint changed - check service documentation 3. Network connectivity issue - test with kubectl exec -n homepage deployment/homepage -- wget -O- SERVICE_URL

Kubernetes Widget Shows TypeError

Symptoms: Red error box with "TypeError: Cannot read properties of undefined"

Cause: Homepage v1.8.0+ requires both cluster and nodes sections in Kubernetes widget

Fix: Ensure widget config includes both sections (see Kubernetes Widget example above)

SealedSecret Won't Decrypt

Symptoms: Pod events show "MountVolume.SetUp failed"

Common Causes:

  1. Wrong sealing certificate used
  2. Namespace mismatch (secret sealed for different namespace)
  3. SealedSecrets controller not running

Fix:

# Verify SealedSecrets controller is running
kubectl get pods -n kube-system | grep sealed-secrets

# Check certificate path
ls -la /Users/dskaggs/.ssh/k8s.crt

# Recreate SealedSecret with correct cert and namespace
kubectl create secret generic homepage-SERVICE-api \
  --from-literal=SERVICE_API_KEY=YOUR_KEY \
  --namespace=homepage \
  --dry-run=client -o yaml | \
  kubeseal --cert /Users/dskaggs/.ssh/k8s.crt --format yaml > sealedsecret-SERVICE.yaml

Security Considerations

  • API Keys: Never commit plaintext API keys to Git
  • SealedSecrets: Encrypted secrets are safe to commit
  • Service URLs: Use cluster-internal URLs (no external exposure needed)
  • RBAC: Homepage ServiceAccount has minimal permissions (read-only cluster metrics)
  • Authentication: All access protected by Authelia SSO

Maintenance

Updating Homepage

# Update image tag in flux-repo/apps/_bases/homepage/helmrelease.yaml
image:
  repository: ghcr.io/gethomepage/homepage
  tag: v1.9.0  # Update version

# Commit and push
git add apps/_bases/homepage/helmrelease.yaml
git commit -m "Update Homepage to v1.9.0"
git push

Adding New Services

Follow the "Adding Service Widgets" section above. Each new service requires:

  1. One new SealedSecret file
  2. One line added to overlay kustomization resources
  3. One line added to HelmRelease envFrom
  4. Widget configuration in services section

No need to touch existing secrets or configurations.