From b87f62e0f57c22b8975adda029a96e6a10b1a6e1 Mon Sep 17 00:00:00 2001 From: Erich Blume Date: Sun, 10 May 2026 20:32:38 -0700 Subject: [PATCH 1/2] C1: nix-build homepage container for amd64 ringtail migration Replace Dockerfile (arm64-only, indri-built) with a nix derivation adapted from nixpkgs pkgs/by-name/ho/homepage-dashboard. Built via the nix-container-builder runner on ringtail, producing an amd64 image suitable for k3s. Includes the upstream Next.js file-system-cache patch to avoid prerender cache write failures on a read-only nix store path (nixpkgs issues #328621 and #458494). Pinned to v1.11.0 (current production version). --- containers/homepage/Dockerfile | 47 ------------- containers/homepage/default.nix | 119 ++++++++++++++++++++++++++++++++ 2 files changed, 119 insertions(+), 47 deletions(-) delete mode 100644 containers/homepage/Dockerfile create mode 100644 containers/homepage/default.nix diff --git a/containers/homepage/Dockerfile b/containers/homepage/Dockerfile deleted file mode 100644 index 6e53e1c..0000000 --- a/containers/homepage/Dockerfile +++ /dev/null @@ -1,47 +0,0 @@ -# Homepage - self-hosted services dashboard -# Two-stage build: Node.js build, Alpine runtime - -ARG CONTAINER_APP_VERSION=v1.11.0 -ARG HOMEPAGE_VERSION=${CONTAINER_APP_VERSION} - -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/mirrors/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 - -ARG CONTAINER_APP_VERSION -LABEL org.opencontainers.image.title="Homepage" -LABEL org.opencontainers.image.description="A self-hosted services landing page" -LABEL org.opencontainers.image.version="${CONTAINER_APP_VERSION}" -LABEL org.opencontainers.image.source="https://forge.eblu.me/eblume/blumeops" -LABEL org.opencontainers.image.vendor="blumeops" - -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/containers/homepage/default.nix b/containers/homepage/default.nix new file mode 100644 index 0000000..7b4becb --- /dev/null +++ b/containers/homepage/default.nix @@ -0,0 +1,119 @@ +# Nix-built gethomepage/homepage dashboard +# Builds v1.11.0 from forge mirror. +# +# Adapted from nixpkgs pkgs/by-name/ho/homepage-dashboard (commit master), +# changed to fetch from our forge mirror and wrap with dockerTools for an +# amd64 image runnable on ringtail's k3s. +# +# The preBuild substitutions are not optional — without them Next.js writes +# its file-system-cache to a read-only path and prerender state breaks after +# restart (nixpkgs issues #328621 and #458494). +{ pkgs ? import { } }: + +let + version = "1.11.0"; + + homepage = pkgs.stdenv.mkDerivation (finalAttrs: { + pname = "homepage-dashboard"; + inherit version; + + src = pkgs.fetchgit { + url = "https://forge.ops.eblu.me/mirrors/homepage.git"; + rev = "v${version}"; + hash = "sha256-jnv9PnClm/jIQ4uU6c4A1UiAmwoihG0l6k3fUbD47I4="; + }; + + pnpmDeps = pkgs.fetchPnpmDeps { + inherit (finalAttrs) pname version src; + pnpm = pkgs.pnpm_10; + fetcherVersion = 3; + hash = "sha256-X5j9XppbcasGuC7fUsj4XzbaQFM9WcRcXjgJHN/inR8="; + }; + + nativeBuildInputs = [ + pkgs.makeBinaryWrapper + pkgs.nodejs_24 + pkgs.pnpmConfigHook + pkgs.pnpm_10 + ]; + + buildInputs = [ + pkgs.nodePackages.node-gyp-build + ]; + + env.PYTHON = "${pkgs.python3}/bin/python"; + + preBuild = '' + substituteInPlace node_modules/next/dist/server/lib/incremental-cache/file-system-cache.js \ + --replace-fail 'this.serverDistDir = ctx.serverDistDir;' \ + 'this.serverDistDir = require("path").join((process.env.NIXPKGS_HOMEPAGE_CACHE_DIR || "/tmp/homepage-cache"), "homepage");' + + for bundle in node_modules/next/dist/compiled/next-server/*.runtime.prod.js; do + substituteInPlace "$bundle" \ + --replace-fail 'this.serverDistDir=e.serverDistDir' \ + 'this.serverDistDir=(process.env.NIXPKGS_HOMEPAGE_CACHE_DIR||"/tmp/homepage-cache")+"/homepage"' + done + ''; + + buildPhase = '' + runHook preBuild + mkdir -p config + pnpm build + runHook postBuild + ''; + + installPhase = '' + runHook preInstall + + mkdir -p $out/{bin,share} + cp -r .next/standalone $out/share/homepage/ + cp -r public $out/share/homepage/public + chmod +x $out/share/homepage/server.js + + mkdir -p $out/share/homepage/.next + cp -r .next/static $out/share/homepage/.next/static + + makeWrapper "${pkgs.lib.getExe pkgs.nodejs_24}" $out/bin/homepage \ + --set-default PORT 3000 \ + --set-default HOMEPAGE_CONFIG_DIR /app/config \ + --set-default NIXPKGS_HOMEPAGE_CACHE_DIR /tmp/homepage-cache \ + --add-flags "$out/share/homepage/server.js" \ + --prefix PATH : "${pkgs.lib.makeBinPath [ pkgs.unixtools.ping ]}" + + runHook postInstall + ''; + + doDist = false; + }); +in + +pkgs.dockerTools.buildLayeredImage { + name = "blumeops/homepage"; + contents = [ + homepage + pkgs.cacert + pkgs.tzdata + ]; + + extraCommands = '' + mkdir -p tmp + chmod 1777 tmp + ''; + + config = { + Entrypoint = [ "${homepage}/bin/homepage" ]; + Env = [ + "SSL_CERT_FILE=${pkgs.cacert}/etc/ssl/certs/ca-bundle.crt" + "TZDIR=${pkgs.tzdata}/share/zoneinfo" + "TMPDIR=/tmp" + "NIXPKGS_HOMEPAGE_CACHE_DIR=/tmp/homepage-cache" + "HOMEPAGE_CONFIG_DIR=/app/config" + "NEXT_TELEMETRY_DISABLED=1" + "PORT=3000" + ]; + ExposedPorts = { + "3000/tcp" = { }; + }; + User = "1000"; + }; +} -- 2.50.1 (Apple Git-155) From be54cc341179c60e1518aef194275ea3689d0447 Mon Sep 17 00:00:00 2001 From: Erich Blume Date: Sun, 10 May 2026 20:37:03 -0700 Subject: [PATCH 2/2] C1: migrate homepage dashboard to ringtail k3s Repoint the ArgoCD Application destination from minikube to ringtail and bump the image tag to the new amd64 nix-built v1.11.0-b87f62e-nix. Rework services.yaml for the autodiscovery shift: 11 services that previously auto-populated via minikube Ingress annotations (ArgoCD, Immich, Kiwix, Mealie, Miniflux, Grafana, Prometheus, Navidrome, Paperless, TeslaMate, Transmission) become explicit static entries with their widget configs preserved. Conversely, the ringtail services that will now auto-populate (Frigate/NVR, Authentik, Ntfy) are removed from the static list to avoid duplicates; Ollama becomes newly visible. Add a Content group for Immich/Kiwix/Miniflux which previously lived under the autodiscovered "Content" group from annotations. --- argocd/apps/homepage.yaml | 2 +- argocd/manifests/homepage/kustomization.yaml | 2 +- argocd/manifests/homepage/services.yaml | 77 ++++++++++++++++--- .../changelog.d/homepage-to-ringtail.infra.md | 8 ++ 4 files changed, 75 insertions(+), 14 deletions(-) create mode 100644 docs/changelog.d/homepage-to-ringtail.infra.md diff --git a/argocd/apps/homepage.yaml b/argocd/apps/homepage.yaml index 86a0f8d..22147f2 100644 --- a/argocd/apps/homepage.yaml +++ b/argocd/apps/homepage.yaml @@ -14,7 +14,7 @@ spec: targetRevision: main path: argocd/manifests/homepage destination: - server: https://kubernetes.default.svc + server: https://ringtail.tail8d86e.ts.net:6443 namespace: homepage syncPolicy: syncOptions: diff --git a/argocd/manifests/homepage/kustomization.yaml b/argocd/manifests/homepage/kustomization.yaml index 27de0eb..ce627ac 100644 --- a/argocd/manifests/homepage/kustomization.yaml +++ b/argocd/manifests/homepage/kustomization.yaml @@ -17,7 +17,7 @@ resources: images: - name: registry.ops.eblu.me/blumeops/homepage - newTag: v1.11.0-e375859 + newTag: v1.11.0-b87f62e-nix configMapGenerator: - name: homepage-config diff --git a/argocd/manifests/homepage/services.yaml b/argocd/manifests/homepage/services.yaml index 211e043..d552ff2 100644 --- a/argocd/manifests/homepage/services.yaml +++ b/argocd/manifests/homepage/services.yaml @@ -1,3 +1,6 @@ +# Homepage runs on ringtail (k3s) — its k8s autodiscovery only sees ringtail +# Ingresses (frigate→NVR, authentik, ntfy, ollama). Services that live on +# minikube (and indri-native) need explicit static entries here. - Host Services: - Forgejo: href: https://forge.eblu.me @@ -57,10 +60,6 @@ # type: caddy # url: http://indri.tail8d86e.ts.net:2019 - Home: - - NVR: - href: https://nvr.ops.eblu.me - icon: frigate.png - description: Network video recorder - Jellyfin: href: https://jellyfin.ops.eblu.me icon: jellyfin @@ -72,15 +71,61 @@ enableBlocks: true enableNowPlaying: false fields: ["movies", "series", "episodes"] + - Mealie: + href: https://meals.ops.eblu.me + icon: mealie.png + description: Recipe manager + - DJ: + href: https://dj.ops.eblu.me + icon: navidrome.png + description: Music streaming server + widget: + type: navidrome + url: https://dj.ops.eblu.me + user: "{{HOMEPAGE_VAR_NAVIDROME_USER}}" + token: "{{HOMEPAGE_VAR_NAVIDROME_TOKEN}}" + salt: "{{HOMEPAGE_VAR_NAVIDROME_SALT}}" + - Paperless: + href: https://paperless.ops.eblu.me + icon: paperless-ngx.png + description: Document management +- Content: + - Immich: + href: https://photos.ops.eblu.me + icon: immich.png + description: Photo management + - Kiwix: + href: https://kiwix.ops.eblu.me + icon: kiwix.png + description: Offline Wikipedia + - Miniflux: + href: https://feed.ops.eblu.me + icon: miniflux.png + description: RSS reader + widget: + type: miniflux + url: https://feed.ops.eblu.me + key: "{{HOMEPAGE_VAR_MINIFLUX_API_KEY}}" + fields: ["unread"] - Infrastructure: - - Authentik: - href: https://authentik.ops.eblu.me - icon: authentik - description: Identity provider - - Ntfy: - href: https://ntfy.ops.eblu.me - icon: ntfy.png - description: Push notifications + - ArgoCD: + href: https://argocd.ops.eblu.me + icon: argo-cd.png + description: GitOps CD + - Grafana: + href: https://grafana.ops.eblu.me + icon: grafana.png + description: Metrics dashboards + widget: + type: grafana + url: https://grafana.ops.eblu.me + username: "{{HOMEPAGE_VAR_GRAFANA_USERNAME}}" + password: "{{HOMEPAGE_VAR_GRAFANA_PASSWORD}}" + fields: ["dashboards", "totalalerts", "alertstriggered"] + - Prometheus: + href: https://prometheus.ops.eblu.me + icon: prometheus.png + description: Metrics storage - Services: # CV and Docs were previously auto-discovered from k8s Ingresses; after # the indri-native migration ([[cv-on-indri]], [[docs-on-indri]]) there @@ -93,3 +138,11 @@ href: https://docs.eblu.me icon: mdi-book-open-page-variant description: BlumeOps Documentation + - TeslaMate: + href: https://tesla.ops.eblu.me + icon: teslamate.png + description: Tesla data logger + - Transmission: + href: https://torrent.ops.eblu.me + icon: transmission.png + description: Torrent client diff --git a/docs/changelog.d/homepage-to-ringtail.infra.md b/docs/changelog.d/homepage-to-ringtail.infra.md new file mode 100644 index 0000000..1e3e795 --- /dev/null +++ b/docs/changelog.d/homepage-to-ringtail.infra.md @@ -0,0 +1,8 @@ +Migrated homepage dashboard from minikube (indri/arm64) to k3s (ringtail/amd64). +The container is now built via nix (`containers/homepage/default.nix`), adapted +from nixpkgs `homepage-dashboard` with the upstream Next.js cache patches and +wrapped with `dockerTools.buildLayeredImage`. Autodiscovery shifts: services on +minikube (ArgoCD, Immich, Kiwix, Mealie, Miniflux, Grafana, Prometheus, +Navidrome, Paperless, TeslaMate, Transmission) become explicit static entries +in `services.yaml`; ringtail services (Authentik, Frigate/NVR, Ntfy, Ollama) +auto-populate via Ingress annotations. -- 2.50.1 (Apple Git-155)