diff --git a/argocd/apps/homepage.yaml b/argocd/apps/homepage.yaml index 1748990..86a0f8d 100644 --- a/argocd/apps/homepage.yaml +++ b/argocd/apps/homepage.yaml @@ -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 diff --git a/argocd/manifests/homepage/clusterrole.yaml b/argocd/manifests/homepage/clusterrole.yaml new file mode 100644 index 0000000..2738755 --- /dev/null +++ b/argocd/manifests/homepage/clusterrole.yaml @@ -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"] diff --git a/argocd/manifests/homepage/clusterrolebinding.yaml b/argocd/manifests/homepage/clusterrolebinding.yaml new file mode 100644 index 0000000..4b0613d --- /dev/null +++ b/argocd/manifests/homepage/clusterrolebinding.yaml @@ -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 diff --git a/argocd/manifests/homepage/values.yaml b/argocd/manifests/homepage/configmap.yaml similarity index 68% rename from argocd/manifests/homepage/values.yaml rename to argocd/manifests/homepage/configmap.yaml index 151c46e..b74c96a 100644 --- a/argocd/manifests/homepage/values.yaml +++ b/argocd/manifests/homepage/configmap.yaml @@ -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: diff --git a/argocd/manifests/homepage/deployment.yaml b/argocd/manifests/homepage/deployment.yaml new file mode 100644 index 0000000..bc04326 --- /dev/null +++ b/argocd/manifests/homepage/deployment.yaml @@ -0,0 +1,131 @@ +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.0.0 + 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/bookmarks.yaml + subPath: bookmarks.yaml + - name: config + mountPath: /app/config/services.yaml + subPath: services.yaml + - name: config + mountPath: /app/config/widgets.yaml + subPath: widgets.yaml + - name: config + mountPath: /app/config/kubernetes.yaml + subPath: kubernetes.yaml + - name: config + mountPath: /app/config/docker.yaml + subPath: docker.yaml + - name: config + mountPath: /app/config/settings.yaml + subPath: settings.yaml + resources: + requests: + memory: "128Mi" + cpu: "100m" + limits: + memory: "512Mi" + cpu: "500m" + livenessProbe: + httpGet: + path: /api/healthcheck + port: 3000 + httpHeaders: + - name: Host + value: go.ops.eblu.me + initialDelaySeconds: 20 + periodSeconds: 30 + readinessProbe: + httpGet: + path: /api/healthcheck + port: 3000 + httpHeaders: + - name: Host + value: go.ops.eblu.me + initialDelaySeconds: 10 + periodSeconds: 10 + volumes: + - name: config + configMap: + name: homepage-config diff --git a/argocd/manifests/homepage/kustomization.yaml b/argocd/manifests/homepage/kustomization.yaml new file mode 100644 index 0000000..eb0d217 --- /dev/null +++ b/argocd/manifests/homepage/kustomization.yaml @@ -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 diff --git a/argocd/manifests/homepage/service.yaml b/argocd/manifests/homepage/service.yaml new file mode 100644 index 0000000..0eb1b89 --- /dev/null +++ b/argocd/manifests/homepage/service.yaml @@ -0,0 +1,12 @@ +apiVersion: v1 +kind: Service +metadata: + name: homepage + namespace: homepage +spec: + selector: + app: homepage + ports: + - name: http + port: 3000 + targetPort: 3000 diff --git a/argocd/manifests/homepage/serviceaccount.yaml b/argocd/manifests/homepage/serviceaccount.yaml new file mode 100644 index 0000000..e2c39c3 --- /dev/null +++ b/argocd/manifests/homepage/serviceaccount.yaml @@ -0,0 +1,5 @@ +apiVersion: v1 +kind: ServiceAccount +metadata: + name: homepage + namespace: homepage diff --git a/containers/homepage/Dockerfile b/containers/homepage/Dockerfile new file mode 100644 index 0000000..b273abb --- /dev/null +++ b/containers/homepage/Dockerfile @@ -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:24-slim AS builder + +ARG HOMEPAGE_VERSION +RUN apt-get update && apt-get install -y --no-install-recommends git ca-certificates \ + && 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:24-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"] diff --git a/docs/changelog.d/feature-homepage-kustomize.infra.md b/docs/changelog.d/feature-homepage-kustomize.infra.md new file mode 100644 index 0000000..24a9a66 --- /dev/null +++ b/docs/changelog.d/feature-homepage-kustomize.infra.md @@ -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. diff --git a/docs/how-to/knowledgebase/review-services.md b/docs/how-to/knowledgebase/review-services.md index 4716bbf..f07a017 100644 --- a/docs/how-to/knowledgebase/review-services.md +++ b/docs/how-to/knowledgebase/review-services.md @@ -1,6 +1,6 @@ --- title: Review Services -modified: 2026-02-16 +modified: 2026-02-19 tags: - how-to - maintenance @@ -62,6 +62,10 @@ Same as ArgoCD, but also check for new chart versions in the mirrored chart repo 3. If upgrading, update the version and dry-run: `mise run provision-indri -- --tags --check --diff` 4. Follow [[add-ansible-role]] patterns for role changes +## Version Tracking Convention + +The `current-version` field in `service-versions.yaml` tracks the **upstream application version**, not the container image tag. For hybrid services, the container image tag (e.g., `v1.0.0`) is decoupled from the contained app version (e.g., `v1.10.1`). This allows container rebuilds (base image updates, build fixes) without implying an upstream version change. + ## Marking a Service as Reviewed After reviewing, edit `service-versions.yaml` (repo root) and update the service entry: diff --git a/service-versions.yaml b/service-versions.yaml index 1a435f5..dee07c7 100644 --- a/service-versions.yaml +++ b/service-versions.yaml @@ -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