Skip to main content

Homer โ€” Self-Hosted Web Dashboard

Homer is a lightweight static dashboard that runs as a pod in your k3s cluster. It gives you a single browser page with tiles linking to every service in your infrastructure โ€” accessible via Tailscale or Cloudflare Tunnel.


What It Looks Likeโ€‹

โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
โ”‚ Mini Cloud Platform โ”‚
โ”‚ Private Bare-Metal Infrastructure โ”‚
โ”œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ค
โ”‚ MAAS โ”‚ Grafana โ”‚ ArgoCD โ”‚ GitLab โ”‚
โ”‚ Bare-metal โ”‚ Monitoring โ”‚ GitOps โ”‚ CI/CD โ”‚
โ”‚ provisioner โ”‚ dashboards โ”‚ sync โ”‚ โ”‚
โ”œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ดโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ดโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ดโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ค
โ”‚ Kubernetes โ”‚
โ”‚ set-hog (CP) ยท fast-skunk ยท fast-heron โ”‚
โ”‚ โ— โ— โ— All nodes Ready โ”‚
โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜

Deploy Homer in k3sโ€‹

Create the namespace and configโ€‹

kubectl create namespace homer

Create the Homer config file (you'll mount it as a ConfigMap):

cat <<'EOF' > homer-config.yml
title: "Mini Cloud Platform"
subtitle: "Private Bare-Metal Infrastructure"
logo: "logo.png"

header: true
footer: false

colors:
light:
highlight-primary: "#3367d6"
highlight-secondary: "#4285f4"
highlight-hover: "#5a95f5"
background: "#f2f4f8"
card-background: "#ffffff"
text: "#363636"
text-header: "#ffffff"
text-title: "#303030"
text-subtitle: "#424242"
card-shadow: rgba(0, 0, 0, 0.1)
link: "#3273dc"
link-hover: "#363636"
dark:
highlight-primary: "#3367d6"
highlight-secondary: "#4285f4"
highlight-hover: "#5a95f5"
background: "#131313"
card-background: "#2b2b2b"
text: "#eaeaea"
text-header: "#ffffff"
text-title: "#fafafa"
text-subtitle: "#f5f5f5"
card-shadow: rgba(0, 0, 0, 0.4)
link: "#3273dc"
link-hover: "#ffdd57"

services:
- name: "Infrastructure"
icon: "fas fa-server"
items:
- name: "MAAS"
logo: "https://assets.ubuntu.com/v1/0b5f1b3e-maas-logo.svg"
subtitle: "Bare-metal provisioning"
tag: "infra"
url: "http://10.0.0.1:5240/MAAS"
target: "_blank"

- name: "set-hog"
icon: "fas fa-microchip"
subtitle: "Control plane โ€” 10.0.0.2"
tag: "k8s"
url: "http://10.0.0.1:5240/MAAS/r/machines"
target: "_blank"

- name: "fast-skunk"
icon: "fas fa-microchip"
subtitle: "Worker โ€” 10.0.0.4"
tag: "k8s"
url: "http://10.0.0.1:5240/MAAS/r/machines"
target: "_blank"

- name: "fast-heron"
icon: "fas fa-microchip"
subtitle: "Worker โ€” 10.0.0.7"
tag: "k8s"
url: "http://10.0.0.1:5240/MAAS/r/machines"
target: "_blank"

- name: "Observability"
icon: "fas fa-chart-line"
items:
- name: "Grafana"
logo: "https://grafana.com/static/assets/img/fav32.png"
subtitle: "Metrics & dashboards"
tag: "monitoring"
url: "http://10.0.0.2:3000"
target: "_blank"

- name: "Prometheus"
icon: "fas fa-fire"
subtitle: "Metrics storage"
tag: "monitoring"
url: "http://10.0.0.2:9090"
target: "_blank"

- name: "DevOps"
icon: "fas fa-code-branch"
items:
- name: "ArgoCD"
icon: "fas fa-sync"
subtitle: "GitOps โ€” continuous deployment"
tag: "gitops"
url: "http://10.0.0.2:8080"
target: "_blank"

- name: "GitLab / Gitea"
icon: "fas fa-code"
subtitle: "Git platform & CI/CD"
tag: "cicd"
url: "http://10.0.0.2:80"
target: "_blank"
EOF

Create Kubernetes manifestsโ€‹

cat <<'EOF' > homer-deployment.yaml
apiVersion: v1
kind: ConfigMap
metadata:
name: homer-config
namespace: homer
data:
config.yml: |
# paste the contents of homer-config.yml here
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: homer
namespace: homer
spec:
replicas: 1
selector:
matchLabels:
app: homer
template:
metadata:
labels:
app: homer
spec:
containers:
- name: homer
image: b4bz/homer:latest
ports:
- containerPort: 8080
volumeMounts:
- name: config
mountPath: /www/assets/config.yml
subPath: config.yml
volumes:
- name: config
configMap:
name: homer-config
---
apiVersion: v1
kind: Service
metadata:
name: homer
namespace: homer
spec:
type: NodePort
selector:
app: homer
ports:
- port: 8080
targetPort: 8080
nodePort: 30902
EOF

kubectl apply -f homer-deployment.yaml

Verifyโ€‹

kubectl get pods -n homer
kubectl get svc -n homer

Homer is now running at:

http://10.0.0.2:30902 โ† from inside the network
http://100.x.x.x:30902 โ† via Tailscale
https://dashboard.yourdomain.com โ† via Cloudflare Tunnel

Connect to Cloudflare Tunnelโ€‹

Update your ~/.cloudflared/config.yml on the MAAS controller:

- hostname: dashboard.yourdomain.com
service: http://10.0.0.2:30902

Then reload:

sudo systemctl restart cloudflared

Connect to Tailscaleโ€‹

No extra config needed. If the MAAS controller has the 10.0.0.0/24 route advertised via Tailscale, Homer is automatically reachable at:

http://10.0.0.2:30902

from any Tailscale-connected machine.


Customize Homerโ€‹

Edit homer-config.yml and update the ConfigMap:

kubectl create configmap homer-config \
--from-file=config.yml=homer-config.yml \
-n homer \
--dry-run=client -o yaml | kubectl apply -f -

kubectl rollout restart deployment/homer -n homer

Alternative Dashboardsโ€‹

ToolDocker imageFeatures
Homerb4bz/homerLightweight, YAML config, no DB
Dashylissy93/dashyRich UI, status checks, widgets
Heimdalllinuxserver/heimdallApp launcher, search bar
Flamepawelmalak/flameBookmarks + weather widgets

Homer is recommended for simplicity โ€” no database, purely static, minimal resources (~10MB RAM).


Done Whenโ€‹

โœ” Homer pod Running in homer namespace
โœ” Dashboard accessible at NodePort :30902
โœ” All service tiles open correct URLs
โœ” Accessible via Tailscale AND/OR Cloudflare Tunnel