Replace Homepage Helm chart with kustomize manifests and custom Dockerfile

The third-party Helm chart (jameswynn/homepage v2.1.0) pinned Homepage at
v1.2.0, 8 minor versions behind upstream. Replace with plain kustomize
manifests and a Dockerfile building from forge mirror at v1.10.1, matching
the pattern used by other blumeops services.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
Erich Blume 2026-02-19 17:55:00 -08:00
commit 0648ae450d
11 changed files with 267 additions and 126 deletions

View file

@ -1,9 +1,7 @@
# Homepage - Service Dashboard / Start Page
#
# Replaced hajimari with gethomepage for active maintenance and better features.
# Auto-discovers k8s services via ingress annotations.
#
# Helm chart: https://github.com/jameswynn/helm-charts/tree/main/charts/homepage
# Custom container built from gethomepage/homepage, kustomize manifests.
# Dashboard at go.ops.eblu.me / go.tail8d86e.ts.net
apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
@ -11,25 +9,10 @@ metadata:
namespace: argocd
spec:
project: default
sources:
# Helm chart
- repoURL: https://jameswynn.github.io/helm-charts
chart: homepage
targetRevision: 2.1.0
helm:
releaseName: homepage
valueFiles:
- $values/argocd/manifests/homepage/values.yaml
# Values file reference
- repoURL: ssh://forgejo@forge.ops.eblu.me:2222/eblume/blumeops.git
targetRevision: main
ref: values
# Extra manifests (ExternalSecrets, etc)
- repoURL: ssh://forgejo@forge.ops.eblu.me:2222/eblume/blumeops.git
targetRevision: main
path: argocd/manifests/homepage
directory:
exclude: "values.yaml"
source:
repoURL: ssh://forgejo@forge.ops.eblu.me:2222/eblume/blumeops.git
targetRevision: main
path: argocd/manifests/homepage
destination:
server: https://kubernetes.default.svc
namespace: homepage

View file

@ -0,0 +1,23 @@
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
name: homepage
rules:
- apiGroups: [""]
resources: ["namespaces", "pods", "nodes"]
verbs: ["get", "list"]
- apiGroups: ["extensions", "networking.k8s.io"]
resources: ["ingresses"]
verbs: ["get", "list"]
- apiGroups: ["traefik.containo.us", "traefik.io"]
resources: ["ingressroutes"]
verbs: ["get", "list"]
- apiGroups: ["gateway.networking.k8s.io"]
resources: ["gateways", "httproutes"]
verbs: ["get", "list"]
- apiGroups: ["metrics.k8s.io"]
resources: ["nodes", "pods"]
verbs: ["get", "list"]
- apiGroups: ["apiextensions.k8s.io"]
resources: ["customresourcedefinitions/status"]
verbs: ["get"]

View file

@ -0,0 +1,12 @@
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
name: homepage
roleRef:
apiGroup: rbac.authorization.k8s.io
kind: ClusterRole
name: homepage
subjects:
- kind: ServiceAccount
name: homepage
namespace: homepage

View file

@ -1,76 +1,30 @@
# Homepage values for blumeops
# Service dashboard at go.ops.eblu.me
# Homepage configuration files
# Extracted from former Helm values.yaml
apiVersion: v1
kind: ConfigMap
metadata:
name: homepage-config
namespace: homepage
data:
bookmarks.yaml: |
- Admin:
- Tailscale Admin:
- href: https://login.tailscale.com/admin
icon: tailscale
- 1Password:
- href: https://my.1password.com
icon: 1password
- Pulumi:
- href: https://app.pulumi.com/eblume/blumeops-tailnet
icon: si-pulumi
- ArgoCD:
- href: https://argocd.ops.eblu.me
icon: argo-cd
- UniFi:
- href: https://unifi.ui.com
icon: ubiquiti
# Enable RBAC for Kubernetes service autodiscovery
enableRbac: true
serviceAccount:
create: true
# Tailscale Ingress is managed separately in ingress-tailscale.yaml
# (Helm chart template doesn't support tailscale.com/* annotations)
ingress:
main:
enabled: false
env:
- name: HOMEPAGE_ALLOWED_HOSTS
value: "go.tail8d86e.ts.net,go.ops.eblu.me"
# Weather widget
- name: HOMEPAGE_VAR_OPENWEATHERMAP_API_KEY
valueFrom:
secretKeyRef:
name: homepage-openweathermap
key: apikey
# Jellyfin widget
- name: HOMEPAGE_VAR_JELLYFIN_API_KEY
valueFrom:
secretKeyRef:
name: homepage-jellyfin
key: apikey
# Miniflux widget
- name: HOMEPAGE_VAR_MINIFLUX_API_KEY
valueFrom:
secretKeyRef:
name: homepage-miniflux
key: apikey
# Grafana widget
- name: HOMEPAGE_VAR_GRAFANA_USERNAME
valueFrom:
secretKeyRef:
name: homepage-grafana
key: username
- name: HOMEPAGE_VAR_GRAFANA_PASSWORD
valueFrom:
secretKeyRef:
name: homepage-grafana
key: password
# Forgejo widget
- name: HOMEPAGE_VAR_FORGEJO_API_KEY
valueFrom:
secretKeyRef:
name: homepage-forgejo
key: apikey
# Navidrome widget
- name: HOMEPAGE_VAR_NAVIDROME_USER
valueFrom:
secretKeyRef:
name: homepage-navidrome
key: user
- name: HOMEPAGE_VAR_NAVIDROME_SALT
valueFrom:
secretKeyRef:
name: homepage-navidrome
key: salt
- name: HOMEPAGE_VAR_NAVIDROME_TOKEN
valueFrom:
secretKeyRef:
name: homepage-navidrome
key: token
config:
# Host services (non-k8s, on indri or LAN)
services:
services.yaml: |
- Host Services:
- Forgejo:
href: https://forge.ops.eblu.me
@ -134,8 +88,6 @@ config:
# widget:
# type: caddy
# url: http://indri.tail8d86e.ts.net:2019
# Services on ringtail k3s (not autodiscovered — different cluster)
- Infrastructure:
- NVR:
href: https://nvr.ops.eblu.me
@ -146,27 +98,7 @@ config:
icon: ntfy.png
description: Push notifications
# External bookmarks
bookmarks:
- Admin:
- Tailscale Admin:
- href: https://login.tailscale.com/admin
icon: tailscale
- 1Password:
- href: https://my.1password.com
icon: 1password
- Pulumi:
- href: https://app.pulumi.com/eblume/blumeops-tailnet
icon: si-pulumi
- ArgoCD:
- href: https://argocd.ops.eblu.me
icon: argo-cd
- UniFi:
- href: https://unifi.ui.com
icon: ubiquiti
# Widgets on the page (info bar at top)
widgets:
widgets.yaml: |
- greeting:
text_size: xl
text: Welcome to Blue Mops
@ -194,12 +126,12 @@ config:
# url: http://indri.tail8d86e.ts.net:61208
# metric: cpu
# Kubernetes autodiscovery
kubernetes:
kubernetes.yaml: |
mode: cluster
# Layout and styling
settingsString: |
docker.yaml: ""
settings.yaml: |
title: BlumeOps
headerStyle: boxed
quicklaunch:

View file

@ -0,0 +1,113 @@
apiVersion: apps/v1
kind: Deployment
metadata:
name: homepage
namespace: homepage
spec:
replicas: 1
selector:
matchLabels:
app: homepage
template:
metadata:
labels:
app: homepage
spec:
serviceAccountName: homepage
securityContext:
runAsUser: 1000
runAsGroup: 1000
fsGroup: 1000
containers:
- name: homepage
image: registry.ops.eblu.me/blumeops/homepage:v1.10.1
securityContext:
runAsNonRoot: true
allowPrivilegeEscalation: false
ports:
- containerPort: 3000
name: http
env:
- name: HOMEPAGE_ALLOWED_HOSTS
value: "go.tail8d86e.ts.net,go.ops.eblu.me"
# Weather widget
- name: HOMEPAGE_VAR_OPENWEATHERMAP_API_KEY
valueFrom:
secretKeyRef:
name: homepage-openweathermap
key: apikey
# Jellyfin widget
- name: HOMEPAGE_VAR_JELLYFIN_API_KEY
valueFrom:
secretKeyRef:
name: homepage-jellyfin
key: apikey
# Miniflux widget
- name: HOMEPAGE_VAR_MINIFLUX_API_KEY
valueFrom:
secretKeyRef:
name: homepage-miniflux
key: apikey
# Grafana widget
- name: HOMEPAGE_VAR_GRAFANA_USERNAME
valueFrom:
secretKeyRef:
name: homepage-grafana
key: username
- name: HOMEPAGE_VAR_GRAFANA_PASSWORD
valueFrom:
secretKeyRef:
name: homepage-grafana
key: password
# Forgejo widget
- name: HOMEPAGE_VAR_FORGEJO_API_KEY
valueFrom:
secretKeyRef:
name: homepage-forgejo
key: apikey
# Navidrome widget
- name: HOMEPAGE_VAR_NAVIDROME_USER
valueFrom:
secretKeyRef:
name: homepage-navidrome
key: user
- name: HOMEPAGE_VAR_NAVIDROME_SALT
valueFrom:
secretKeyRef:
name: homepage-navidrome
key: salt
- name: HOMEPAGE_VAR_NAVIDROME_TOKEN
valueFrom:
secretKeyRef:
name: homepage-navidrome
key: token
volumeMounts:
- name: config
mountPath: /app/config
- name: logs
mountPath: /app/config/logs
resources:
requests:
memory: "128Mi"
cpu: "100m"
limits:
memory: "512Mi"
cpu: "500m"
livenessProbe:
httpGet:
path: /api/healthcheck
port: 3000
initialDelaySeconds: 20
periodSeconds: 30
readinessProbe:
httpGet:
path: /api/healthcheck
port: 3000
initialDelaySeconds: 10
periodSeconds: 10
volumes:
- name: config
configMap:
name: homepage-config
- name: logs
emptyDir: {}

View file

@ -0,0 +1,17 @@
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
namespace: homepage
resources:
- serviceaccount.yaml
- clusterrole.yaml
- clusterrolebinding.yaml
- configmap.yaml
- deployment.yaml
- service.yaml
- ingress-tailscale.yaml
- external-secret-openweathermap.yaml
- external-secret-jellyfin.yaml
- external-secret-forgejo.yaml
- external-secret-grafana.yaml
- external-secret-miniflux.yaml
- external-secret-navidrome.yaml

View file

@ -0,0 +1,12 @@
apiVersion: v1
kind: Service
metadata:
name: homepage
namespace: homepage
spec:
selector:
app: homepage
ports:
- name: http
port: 3000
targetPort: 3000

View file

@ -0,0 +1,5 @@
apiVersion: v1
kind: ServiceAccount
metadata:
name: homepage
namespace: homepage

View file

@ -0,0 +1,43 @@
# Homepage - self-hosted services dashboard
# Two-stage build: Node.js build, Alpine runtime
ARG HOMEPAGE_VERSION=v1.10.1
FROM node:22-slim AS builder
ARG HOMEPAGE_VERSION
RUN apt-get update && apt-get install -y --no-install-recommends git \
&& rm -rf /var/lib/apt/lists/*
RUN git clone --depth 1 --branch ${HOMEPAGE_VERSION} \
https://forge.ops.eblu.me/eblume/homepage.git /app
WORKDIR /app
RUN mkdir -p config \
&& corepack enable && corepack prepare pnpm@latest --activate \
&& pnpm install --frozen-lockfile \
&& NEXT_TELEMETRY_DISABLED=1 pnpm run build
FROM node:22-alpine
LABEL org.opencontainers.image.title=Homepage
LABEL org.opencontainers.image.description="A self-hosted services landing page"
LABEL org.opencontainers.image.source=https://github.com/gethomepage/homepage
WORKDIR /app
COPY --from=builder --chown=1000:1000 /app/public ./public
COPY --from=builder --chown=1000:1000 /app/.next/standalone/ ./
COPY --from=builder --chown=1000:1000 /app/.next/static/ ./.next/static
RUN mkdir -p /app/config && chown 1000:1000 /app/config
ENV NODE_ENV=production
ENV PORT=3000
EXPOSE 3000
HEALTHCHECK --interval=10s --timeout=3s --start-period=20s \
CMD wget --no-verbose --tries=1 --spider http://127.0.0.1:3000/api/healthcheck || exit 1
USER 1000
CMD ["node", "server.js"]

View file

@ -0,0 +1 @@
Replace Homepage Helm chart (jameswynn/homepage v2.1.0, pinned at app v1.2.0) with plain kustomize manifests and a custom Dockerfile built from upstream v1.10.1. Gives full version control and matches the pattern used by other blumeops services.

View file

@ -45,11 +45,11 @@ services:
upstream-source: https://github.com/binwiederhier/ntfy/releases
- name: homepage
type: argocd
last-reviewed: null
current-version: null
type: hybrid
last-reviewed: 2026-02-19
current-version: "v1.10.1"
upstream-source: https://github.com/gethomepage/homepage/releases
notes: Deployed via Helm chart
notes: Custom container, kustomize manifests
- name: nvidia-device-plugin
type: argocd