Compare commits
28 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| a28d3bbc33 | |||
| 4ab6dcefd2 | |||
| c24b792808 | |||
| 25a4ada891 | |||
| 6e6005aefd | |||
| ad438a07b3 | |||
| ce6f5c72dd | |||
| 630ee8dd90 | |||
| b1ca9ef19c | |||
| 1c949d3fcc | |||
| cfa6c01768 | |||
| 3474d53ce5 | |||
| ca81b959cc | |||
| 23764dff38 | |||
| 3c4a48d7e5 | |||
| c0a39f5dfc | |||
| a1d7b6e433 | |||
| 45f43da659 | |||
| 290cb8949e | |||
| d933d3cee8 | |||
| baedf9f932 | |||
| b387757e87 | |||
| 3eedbcbe16 | |||
| 9033724b15 | |||
| aae176f1b7 | |||
| c7590e6603 | |||
| 3e486b95f6 | |||
| 08b4be7a5d |
@@ -0,0 +1,20 @@
|
|||||||
|
# Agent Context - Homelab Infra
|
||||||
|
|
||||||
|
Typ: Einstieg/Index · Stand: 2026-06-11 · Status: aktiv
|
||||||
|
|
||||||
|
Einstiegspunkt fuer KI-Agenten (Codex, Gemini u. a.; Claude nutzt zusaetzlich
|
||||||
|
`CLAUDE.md`). Kein eigener Inhalt - nur Pflichtpfade.
|
||||||
|
|
||||||
|
## Vor jeder Arbeit lesen
|
||||||
|
|
||||||
|
1. `docs/AI_CONTEXT.md` - Systembild, harte Regeln, Ausnahmen-Kurzliste
|
||||||
|
2. `HOMELAB_ARCHITECTURE_MASTER_V2.md` - Architektur-Zielbild
|
||||||
|
3. `docs/WORKFLOW.md` - verbindlicher GitOps-/No-Drift-Ablauf
|
||||||
|
4. die betroffene `docker-compose.yml` bzw. das betroffene Runbook (Index: `docs/README.md`)
|
||||||
|
|
||||||
|
## Nicht verhandelbar
|
||||||
|
|
||||||
|
- Keine Secret-Werte lesen, zitieren oder schreiben - nur Namen und Pfade.
|
||||||
|
- Keine Deployments, Host-Hotfixes oder Docker-Schreibbefehle ohne ausdrueckliche Anweisung.
|
||||||
|
- Doku-Regeln aus `docs/REPO_MAP.md` einhalten: ein Fakt, ein Zuhause. Status nur in `docs/MASTER_TODO.md`, Entscheidungen nur in `docs/DECISIONS.md`.
|
||||||
|
- Bei Drift oder zwei fehlgeschlagenen Reparaturversuchen: stoppen, `docs/GITOPS_DRIFT_RUNBOOK.md`.
|
||||||
@@ -93,6 +93,7 @@ Jeder produktive Container nutzt `restart: unless-stopped`, außer eine Ausnahme
|
|||||||
| `monitoring_net` | Compose-intern, bridge | zentraler Observability-Stack fuer Prometheus, Loki, Grafana, Promtail, Exporter und InfluxDB | Zielzustand |
|
| `monitoring_net` | Compose-intern, bridge | zentraler Observability-Stack fuer Prometheus, Loki, Grafana, Promtail, Exporter und InfluxDB | Zielzustand |
|
||||||
| `monitoring_influx_lan` | Compose-intern, bridge | nicht-oeffentliches Zusatznetz nur fuer Docker Host-Port-Publishing von InfluxDB 8181 | Zielzustand |
|
| `monitoring_influx_lan` | Compose-intern, bridge | nicht-oeffentliches Zusatznetz nur fuer Docker Host-Port-Publishing von InfluxDB 8181 | Zielzustand |
|
||||||
| `glance_socket_net` | Compose-intern, `internal: true` | interner Zugriff von Glance auf den Docker-Socket-Proxy | umgesetzt |
|
| `glance_socket_net` | Compose-intern, `internal: true` | interner Zugriff von Glance auf den Docker-Socket-Proxy | umgesetzt |
|
||||||
|
| `smarthome_net` | bridge, `internal: true` | interne Smart-Home-Kommunikation zwischen Home Assistant, Mosquitto, spaeter Zigbee2MQTT/ESPHome | vorbereitet |
|
||||||
| `host` | host | nur für echte Sonderfälle | begründet |
|
| `host` | host | nur für echte Sonderfälle | begründet |
|
||||||
|
|
||||||
### 3.2 Finales Diagramm (vereinfacht)
|
### 3.2 Finales Diagramm (vereinfacht)
|
||||||
@@ -123,7 +124,8 @@ App-interne Netze
|
|||||||
├── immich_default (internal: true) ✅
|
├── immich_default (internal: true) ✅
|
||||||
├── nextcloud_internal (internal: true) ✅
|
├── nextcloud_internal (internal: true) ✅
|
||||||
├── monitoring_net (zentraler Observability-Stack)
|
├── monitoring_net (zentraler Observability-Stack)
|
||||||
└── monitoring_influx_lan (Bridge fuer LAN-Port-Publishing, keine Traefik-Route)
|
├── monitoring_influx_lan (Bridge fuer LAN-Port-Publishing, keine Traefik-Route)
|
||||||
|
└── smarthome_net (HA, Mosquitto, spaeter Zigbee2MQTT/ESPHome)
|
||||||
|
|
||||||
Host-Sonderfälle
|
Host-Sonderfälle
|
||||||
├── tailscale
|
├── tailscale
|
||||||
@@ -146,6 +148,7 @@ Diese Dienste sind über echte `*.kaleschke.info`-Domains erreichbar:
|
|||||||
- `immich_server` — immich.kaleschke.info
|
- `immich_server` — immich.kaleschke.info
|
||||||
- `nextcloud` — cloud.kaleschke.info
|
- `nextcloud` — cloud.kaleschke.info
|
||||||
- `plex` — plex.kaleschke.info (Traefik, native Plex-Auth; Plex Remote Access/Port 32400 bleibt aus)
|
- `plex` — plex.kaleschke.info (Traefik, native Plex-Auth; Plex Remote Access/Port 32400 bleibt aus)
|
||||||
|
- `homeassistant` — home.kaleschke.info (Traefik, native Home-Assistant-Auth)
|
||||||
|
|
||||||
### 4.2 Nicht öffentlich / nur Tailscale oder Traefik + Middleware
|
### 4.2 Nicht öffentlich / nur Tailscale oder Traefik + Middleware
|
||||||
Diese Dienste sind **keine Public Apps**:
|
Diese Dienste sind **keine Public Apps**:
|
||||||
@@ -261,6 +264,7 @@ Legende Status:
|
|||||||
| `immich_redis` | ⏳ | `immich_default` | intern | intern-only | anonymes Volume → named volume |
|
| `immich_redis` | ⏳ | `immich_default` | intern | intern-only | anonymes Volume → named volume |
|
||||||
| `nextcloud-postgres` | ✅ | `nextcloud_internal` | intern | app-eigene Nextcloud-Datenbank mit `_FILE`-Secret | — |
|
| `nextcloud-postgres` | ✅ | `nextcloud_internal` | intern | app-eigene Nextcloud-Datenbank mit `_FILE`-Secret | — |
|
||||||
| `nextcloud-redis` | ✅ | `nextcloud_internal` | intern | app-eigener Cache fuer File Locking / Sessions | — |
|
| `nextcloud-redis` | ✅ | `nextcloud_internal` | intern | app-eigener Cache fuer File Locking / Sessions | — |
|
||||||
|
| `smarthome-mosquitto` | ✅ vorbereitet | `smarthome_net` | intern `1883`, kein Host-Port in Phase 1 | MQTT-Datenbus fuer Home Assistant, spaeter ESPHome und Zigbee2MQTT; Passwortdatei und ACLs in `/mnt/user/appdata/mosquitto/config` | LAN-Port erst in ESPHome-Phase mit ACLs/per-Device-Usern |
|
||||||
|
|
||||||
### 7.4 Produktive Apps
|
### 7.4 Produktive Apps
|
||||||
|
|
||||||
@@ -274,6 +278,7 @@ Legende Status:
|
|||||||
| `immich_server` | ✅ | `immich_default`, `frontend_net` | Traefik | aktiv via `immich.kaleschke.info` | — |
|
| `immich_server` | ✅ | `immich_default`, `frontend_net` | Traefik | aktiv via `immich.kaleschke.info` | — |
|
||||||
| `immich_machine_learning` | ✅ | `immich_default` | intern | bleibt intern | — |
|
| `immich_machine_learning` | ✅ | `immich_default` | intern | bleibt intern | — |
|
||||||
| `nextcloud` | ✅ | `frontend_net`, `nextcloud_internal` | Traefik | aktiv via `cloud.kaleschke.info`, nativer Nextcloud-Login, WebDAV/CardDAV faehig | CalDAV/CardDAV-Redirect via Traefik-Labels |
|
| `nextcloud` | ✅ | `frontend_net`, `nextcloud_internal` | Traefik | aktiv via `cloud.kaleschke.info`, nativer Nextcloud-Login, WebDAV/CardDAV faehig | CalDAV/CardDAV-Redirect via Traefik-Labels |
|
||||||
|
| `homeassistant` | ✅ vorbereitet | `frontend_net`, `smarthome_net` | Traefik via `home.kaleschke.info`, native HA-Auth | Home Assistant Container im GitOps-Stack `smart-home/`; kein HAOS, kein Supervised; Fach-YAML kommt aus `smart-home-kalli`, `.storage` bleibt in `/mnt/user/appdata/homeassistant` | Deploy, Onboarding, Restore-Probe, Cloud-Integrationen |
|
||||||
| `plex` | ✅ | `host` | Traefik via `plex.kaleschke.info` + Plex native Auth; LAN direkt `:32400` | Compose-Stack unter `host-services/plex/`; Host-Netz bleibt fuer Discovery / Plex GDM dokumentierte Ausnahme; Traefik routet per File-Provider-Ausnahme auf `http://192.168.178.58:32400`, weil Docker-Labels Host-Netz-Container aus Traefik heraus auf `127.0.0.1` routen wuerden; kein direkter WAN-Port 32400 und Plex Remote Access bleibt aus; Server geclaimt von `Xeridos`; Smart-TVs (Schlafzimmer, Wohnzimmer) ueber WLAN-LAN per mDNS | — |
|
| `plex` | ✅ | `host` | Traefik via `plex.kaleschke.info` + Plex native Auth; LAN direkt `:32400` | Compose-Stack unter `host-services/plex/`; Host-Netz bleibt fuer Discovery / Plex GDM dokumentierte Ausnahme; Traefik routet per File-Provider-Ausnahme auf `http://192.168.178.58:32400`, weil Docker-Labels Host-Netz-Container aus Traefik heraus auf `127.0.0.1` routen wuerden; kein direkter WAN-Port 32400 und Plex Remote Access bleibt aus; Server geclaimt von `Xeridos`; Smart-TVs (Schlafzimmer, Wohnzimmer) ueber WLAN-LAN per mDNS | — |
|
||||||
| `super-productivity` | ✅ vorbereitet | `frontend_net` | Traefik + Middleware | Persoenliche Task-PWA des Operators; Issues kommen aus Gitea `Micha/mails` via n8n-Mail-Workflow | Deploy + Webhook + DNS-Eintrag offen |
|
| `super-productivity` | ✅ vorbereitet | `frontend_net` | Traefik + Middleware | Persoenliche Task-PWA des Operators; Issues kommen aus Gitea `Micha/mails` via n8n-Mail-Workflow | Deploy + Webhook + DNS-Eintrag offen |
|
||||||
| `n8n` | ✅ vorbereitet | `frontend_net` | Traefik, native Auth (keine pauschale Authelia) | Workflow-Automation; erster Workflow: GMX-Mail -> OpenAI-Extraktion -> Gitea-Issue in `Micha/mails`; `N8N_ENCRYPTION_KEY` ist Stack-ENV-Pflichtsecret | Deploy + Webhook + Owner-Setup offen |
|
| `n8n` | ✅ vorbereitet | `frontend_net` | Traefik, native Auth (keine pauschale Authelia) | Workflow-Automation; erster Workflow: GMX-Mail -> OpenAI-Extraktion -> Gitea-Issue in `Micha/mails`; `N8N_ENCRYPTION_KEY` ist Stack-ENV-Pflichtsecret | Deploy + Webhook + Owner-Setup offen |
|
||||||
@@ -389,10 +394,12 @@ Die Blockmigration aus der Portainer-/Dockerman-Phase ist abgeschlossen: Traefik
|
|||||||
| `mail-archiver` | `frontend_net` + `backend_net` | braucht Internetzugang für IMAP-Abruf (GMX, Gmail) und DB-Zugang |
|
| `mail-archiver` | `frontend_net` + `backend_net` | braucht Internetzugang für IMAP-Abruf (GMX, Gmail) und DB-Zugang |
|
||||||
| `traefik/dynamic/*` | manueller Host-Sync trotz GitOps | File-Provider bleibt bewusst fuer `middlewares.yml`, `tls.yml` und `dashboards.yml`; Komodo deployed diese Dateien nicht automatisch |
|
| `traefik/dynamic/*` | manueller Host-Sync trotz GitOps | File-Provider bleibt bewusst fuer `middlewares.yml`, `tls.yml` und `dashboards.yml`; Komodo deployed diese Dateien nicht automatisch |
|
||||||
| `nextcloud` | keine zentrale ForwardAuth-Middleware | Nextcloud bringt eigene Auth, Clients und WebDAV/CardDAV-Endpunkte mit; Traefik bleibt Reverse Proxy, Auth bleibt app-nativ |
|
| `nextcloud` | keine zentrale ForwardAuth-Middleware | Nextcloud bringt eigene Auth, Clients und WebDAV/CardDAV-Endpunkte mit; Traefik bleibt Reverse Proxy, Auth bleibt app-nativ |
|
||||||
| `monitoring-influxdb3-core` | Host-Port 8181 auf LAN-IP; `user: "0"` | Home Assistant laeuft in einer VM ausserhalb des Compose-Netzes und muss Metriken schreiben koennen; keine Traefik-Route, kein `frontend_net`, Zugriff nur ueber Token und LAN-IP `INFLUXDB_BIND_IP`; InfluxDB 3 Core benoetigt im aktuellen Container-Setup Root-Rechte fuer den lokalen Object-Store-Pfad im named volume |
|
| `monitoring-influxdb3-core` | Host-Port 8181 auf LAN-IP; `user: "0"` | Home Assistant schreibt spaeter Langzeitdaten. Nach der HA-Container-Entscheidung muss der Writer-Pfad in der Influx-Phase explizit gewaehlt werden: entweder LAN-Bind via `INFLUXDB_BIND_IP` oder gezieltes gemeinsames internes Netz. Keine Traefik-Route, Zugriff nur ueber Token; InfluxDB 3 Core benoetigt im aktuellen Container-Setup Root-Rechte fuer den lokalen Object-Store-Pfad im named volume |
|
||||||
| `monitoring-promtail` | Docker-Socket read-only | Docker-Log-Discovery fuer Loki; keine Schreibrechte, keine Appdaten-Persistenz ueber den Socket |
|
| `monitoring-promtail` | Docker-Socket read-only | Docker-Log-Discovery fuer Loki; keine Schreibrechte, keine Appdaten-Persistenz ueber den Socket |
|
||||||
| `n8n` | keine pauschale Authelia-Middleware | Webhook-Endpunkte (`/webhook/*`, `/webhook-test/*`) muessen ohne ForwardAuth erreichbar bleiben; n8n bringt eigene Owner-/Login-Auth mit (analog Komodo/Nextcloud) |
|
| `n8n` | keine pauschale Authelia-Middleware | Webhook-Endpunkte (`/webhook/*`, `/webhook-test/*`) muessen ohne ForwardAuth erreichbar bleiben; n8n bringt eigene Owner-/Login-Auth mit (analog Komodo/Nextcloud) |
|
||||||
| `plex` | Traefik ohne Authelia, File-Provider-Ausnahme trotz Host-Netz | Plex bringt native Konto-/Client-Auth mit; vorgeschaltete ForwardAuth wuerde Plex Web, Apps und Client-Flows stoeren. Docker-Labels sind fuer diesen Host-Netz-Container ungeeignet, weil Traefik sonst `127.0.0.1:32400` nutzt; daher `traefik/dynamic/plex.yml` mit Ziel `192.168.178.58:32400`. Route nur ueber Traefik/443 (`plex.kaleschke.info`), direkter Plex-WAN-Port 32400 und Plex Remote Access bleiben deaktiviert. |
|
| `plex` | Traefik ohne Authelia, File-Provider-Ausnahme trotz Host-Netz | Plex bringt native Konto-/Client-Auth mit; vorgeschaltete ForwardAuth wuerde Plex Web, Apps und Client-Flows stoeren. Docker-Labels sind fuer diesen Host-Netz-Container ungeeignet, weil Traefik sonst `127.0.0.1:32400` nutzt; daher `traefik/dynamic/plex.yml` mit Ziel `192.168.178.58:32400`. Route nur ueber Traefik/443 (`plex.kaleschke.info`), direkter Plex-WAN-Port 32400 und Plex Remote Access bleiben deaktiviert. |
|
||||||
|
| `homeassistant` | Traefik ohne Authelia, Fach-YAML aus separatem Repo | Home Assistant bringt eigene Auth, mobile Apps, Webhooks und Integrationsfluesse mit. Der Container haengt in `frontend_net` fuer Traefik und in `smarthome_net` fuer MQTT/Zigbee2MQTT/ESPHome. `.storage` und Secrets bleiben in Appdata und werden per Borg gesichert, nicht versioniert. |
|
||||||
|
| `Ecowitt` | spaetere HTTP-Ausnahme offen | Ecowitt kann nur HTTP. Wegen globalem Traefik-HTTP-Redirect wird in Phase 2 entschieden, ob Traefik eine selektive Webhook-Ausnahme bekommt oder ob ein LAN-only HA-Port `8123` als dokumentierte Host-Port-Ausnahme noetig wird. |
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
services:
|
services:
|
||||||
immich-server:
|
immich-server:
|
||||||
container_name: immich_server
|
container_name: immich_server
|
||||||
image: ghcr.io/immich-app/immich-server:release@sha256:c15bff75068effb03f4355997d03dc7e0fc58720c2b54ad6f7f10d1bc57efaa5
|
image: ghcr.io/immich-app/immich-server:v2.7.5@sha256:c15bff75068effb03f4355997d03dc7e0fc58720c2b54ad6f7f10d1bc57efaa5
|
||||||
restart: unless-stopped
|
restart: unless-stopped
|
||||||
depends_on:
|
depends_on:
|
||||||
- redis
|
- redis
|
||||||
@@ -32,7 +32,7 @@ services:
|
|||||||
|
|
||||||
immich-machine-learning:
|
immich-machine-learning:
|
||||||
container_name: immich_machine_learning
|
container_name: immich_machine_learning
|
||||||
image: ghcr.io/immich-app/immich-machine-learning:release@sha256:a2501141440f10516d329fdfba2c68082e19eb9ba6016c061ac80d23beadf7f3
|
image: ghcr.io/immich-app/immich-machine-learning:v2.7.5@sha256:a2501141440f10516d329fdfba2c68082e19eb9ba6016c061ac80d23beadf7f3
|
||||||
restart: unless-stopped
|
restart: unless-stopped
|
||||||
environment:
|
environment:
|
||||||
# Workaround fuer gunicorn-25.1.0-Control-Socket-Bug: der Worker haengt
|
# Workaround fuer gunicorn-25.1.0-Control-Socket-Bug: der Worker haengt
|
||||||
|
|||||||
@@ -54,7 +54,7 @@ services:
|
|||||||
- traefik.http.services.mealie.loadbalancer.server.port=9000
|
- traefik.http.services.mealie.loadbalancer.server.port=9000
|
||||||
|
|
||||||
mealie-postgres:
|
mealie-postgres:
|
||||||
image: postgres:18.4@sha256:8ff36f3c66371cba71d20ceedccfc3de9669a68737607888c4ef0af93abe8e39
|
image: postgres:18.4@sha256:29ee7bb30d804447dc9a91fd0d74322ae1dc3a4072cc6346f70a5ed6e783b565
|
||||||
container_name: mealie-postgres
|
container_name: mealie-postgres
|
||||||
restart: unless-stopped
|
restart: unless-stopped
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
services:
|
services:
|
||||||
nextcloud:
|
nextcloud:
|
||||||
image: nextcloud:34.0.0-apache@sha256:e15ff2ff02d04bf272940bb63f2157599bc9440d45b688cc60f09b72be7ae717
|
image: nextcloud:33.0.5-apache@sha256:56bdc45109067500fd0832fa64832b7c77a167d9394cbf5f0f4b59740b94194d
|
||||||
container_name: nextcloud
|
container_name: nextcloud
|
||||||
restart: unless-stopped
|
restart: unless-stopped
|
||||||
depends_on:
|
depends_on:
|
||||||
@@ -46,7 +46,7 @@ services:
|
|||||||
- "traefik.http.services.nextcloud.loadbalancer.server.port=80"
|
- "traefik.http.services.nextcloud.loadbalancer.server.port=80"
|
||||||
|
|
||||||
nextcloud-postgres:
|
nextcloud-postgres:
|
||||||
image: postgres:18.4@sha256:8ff36f3c66371cba71d20ceedccfc3de9669a68737607888c4ef0af93abe8e39
|
image: postgres:18.4@sha256:29ee7bb30d804447dc9a91fd0d74322ae1dc3a4072cc6346f70a5ed6e783b565
|
||||||
container_name: nextcloud-postgres
|
container_name: nextcloud-postgres
|
||||||
restart: unless-stopped
|
restart: unless-stopped
|
||||||
environment:
|
environment:
|
||||||
|
|||||||
@@ -11,6 +11,57 @@ in `HOMELAB_ARCHITECTURE_MASTER_V2.md` §13, `docs/MASTER_TODO.md` (Geparkt),
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
## 2026-06-12 - Home Assistant als Container im GitOps-Stack
|
||||||
|
|
||||||
|
**Entscheidung:** Home Assistant laeuft neu als `homeassistant` Container im
|
||||||
|
Stack `smart-home/`, nicht als HAOS-VM und nicht als Supervised-Installation.
|
||||||
|
Mosquitto laeuft als eigener Container im selben Stack; Zigbee2MQTT und ESPHome
|
||||||
|
werden spaeter ebenfalls als eigenstaendige Container ergaenzt. HA haengt in
|
||||||
|
`frontend_net` fuer Traefik und in `smarthome_net` fuer MQTT/Zigbee2MQTT/ESPHome.
|
||||||
|
Das Fachrepo `smart-home-kalli` liefert versionierte HA-YAML-Dateien read-only;
|
||||||
|
`.storage`, `secrets.yaml` und Integrations-State bleiben in
|
||||||
|
`/mnt/user/appdata/homeassistant`.
|
||||||
|
|
||||||
|
**Kontext:** Das fruehere HAOS-VM-Setup ging bei einem Crash ohne brauchbares
|
||||||
|
Backup verloren. Das Homelab betreibt produktive Dienste inzwischen ueber
|
||||||
|
Gitea, Komodo, Compose, Renovate und Borg. HA Container passt in dieses
|
||||||
|
Betriebsmodell und vermeidet eine zweite Update-/Backup-Welt. Supervised ist
|
||||||
|
kein Zielpfad mehr; HAOS bleibt die Alternative, falls Add-on-Komfort,
|
||||||
|
Matter/Thread/HomeKit-Discovery oder Host-nahe HA-Funktionen wichtiger werden
|
||||||
|
als GitOps-Konformitaet.
|
||||||
|
|
||||||
|
**Review-Trigger:** Viele mDNS-/SSDP-abhaengige lokale Integrationen
|
||||||
|
(HomeKit, Cast, Matter/Thread), Bedarf an HA-Add-ons als Betriebsstandard,
|
||||||
|
oder wiederholte Probleme durch Bridge-Netzwerkbetrieb.
|
||||||
|
|
||||||
|
## 2026-06-12 - Ecowitt-Ingress bleibt bewusste Phase-2-Entscheidung
|
||||||
|
|
||||||
|
**Entscheidung:** In Phase 1 wird kein Host-Port `8123` fuer Home Assistant
|
||||||
|
veroeffentlicht. Ecowitt wird spaeter entweder ueber eine gezielte
|
||||||
|
Traefik-HTTP-Ausnahme fuer den Webhook-Pfad angebunden oder, falls der globale
|
||||||
|
HTTP-zu-HTTPS-EntryPoint-Redirect nicht sauber selektiv abloesbar ist, ueber
|
||||||
|
einen dokumentierten LAN-only Host-Port `8123`.
|
||||||
|
|
||||||
|
**Kontext:** Ecowitt kann nur HTTP und kein HTTPS. Traefik hat aktuell einen
|
||||||
|
globalen `web` -> `websecure` Redirect auf EntryPoint-Ebene. Ein normaler
|
||||||
|
HTTP-Router kann diese Regel voraussichtlich nicht umgehen, ohne Traefik selbst
|
||||||
|
umzubauen. Deshalb wird die Entscheidung nicht vorgezogen.
|
||||||
|
|
||||||
|
**Review-Trigger:** Start der Ecowitt-/InfluxDB-Phase oder Umbau der Traefik
|
||||||
|
HTTP-Redirect-Architektur.
|
||||||
|
|
||||||
|
## 2026-06-11 — Host-DNS-Fallback aktiv (AdGuard-SPOF entschaerft)
|
||||||
|
|
||||||
|
**Entscheidung:** Unraid-Host nutzt `eth0` DNS server 1 = `192.168.178.58` (AdGuard) und **DNS server 2 = `192.168.178.1`** (FRITZ!Box) als Failover.
|
||||||
|
**Kontext:** AdGuard war einziger LAN-Resolver; ein Recreate hat 2026-06 einen Bulk-Deploy zerlegt, weil Docker-Pulls am eigenen DNS-Container scheiterten. Der Fallback bleibt nur passiv aktiv (Go-Resolver springt erst bei Socket-Fehler weiter), der Filter wirkt im Normalbetrieb unveraendert. `options rotate` ist nicht gesetzt. Umsetzung der Empfehlung 3a aus dem Optimierungs-Assessment vom 2026-06-10. Runbook: `docs/runbooks/komodo-bulk-deploy-dns.md`.
|
||||||
|
**Review-Trigger:** Wenn AdGuard durch eine andere Filter-Loesung ersetzt wird oder ein zweiter Host-Resolver verfuegbar ist.
|
||||||
|
|
||||||
|
## 2026-06-11 — Hetzner Storage Box: automatische Snapshots aktiv
|
||||||
|
|
||||||
|
**Entscheidung:** Automatische Snapshots auf der Hetzner Storage Box (BX11, `u565255.your-storagebox.de`) sind aktiv: taeglich um 05:30 UTC (nach dem Borg-Lauf 04:30 lokal), Retention 7 Tage, Snapshot-Verzeichnis sichtbar fuer Einzeldatei-Restore via `.zfs/snapshot/`.
|
||||||
|
**Kontext:** Borg `append-only` ist bewusst nicht umgesetzt (siehe Eintrag 2026-06-01); damit war ein kompromittierter Host bisher in der Lage, auch das Off-site-Backup zu loeschen. Storage-Box-Snapshots sind host-seitig nicht loeschbar und im BX11-Tarif inklusive. Kosten: 0 EUR zusaetzlich. Umsetzung der Empfehlung 2 aus dem Optimierungs-Assessment vom 2026-06-10.
|
||||||
|
**Review-Trigger:** Hetzner-Quota-Druck (aktuell 65 GB / 1 TB - viel Luft) oder Aenderung der Backup-Strategie.
|
||||||
|
|
||||||
## 2026-06-11 — Doku-Konsolidierung: ein Fakt, ein Zuhause
|
## 2026-06-11 — Doku-Konsolidierung: ein Fakt, ein Zuhause
|
||||||
|
|
||||||
**Entscheidung:** Die Dokumentation wird nach `docs/archive/2026/homelab-doku-optimierung-2026-06-11.md` konsolidiert: `MASTER_TODO.md` ist die einzige Statusliste, dieses Register die einzige Entscheidungssammlung, `docs/archive/` nimmt abgeschlossene Snapshots auf, Erledigtes verlaesst die Arbeitskopie. Keine Ordner-Restruktur des Bestands.
|
**Entscheidung:** Die Dokumentation wird nach `docs/archive/2026/homelab-doku-optimierung-2026-06-11.md` konsolidiert: `MASTER_TODO.md` ist die einzige Statusliste, dieses Register die einzige Entscheidungssammlung, `docs/archive/` nimmt abgeschlossene Snapshots auf, Erledigtes verlaesst die Arbeitskopie. Keine Ordner-Restruktur des Bestands.
|
||||||
|
|||||||
+10
-4
@@ -1,6 +1,6 @@
|
|||||||
# Master To-do - KalliLab CORE
|
# Master To-do - KalliLab CORE
|
||||||
|
|
||||||
Typ: Status/To-do · Stand: 2026-06-11 · Status: aktiv
|
Typ: Status/To-do · Stand: 2026-06-12 · Status: aktiv
|
||||||
|
|
||||||
Diese Liste ist die **einzige** Arbeitsliste fuer offene operative Punkte im
|
Diese Liste ist die **einzige** Arbeitsliste fuer offene operative Punkte im
|
||||||
Homelab. Detailablaeufe stehen in den verlinkten Runbooks; Entscheidungen mit
|
Homelab. Detailablaeufe stehen in den verlinkten Runbooks; Entscheidungen mit
|
||||||
@@ -24,6 +24,9 @@ Host-Reports (`/mnt/user/backups/restore-reports/`) und in der Git-Historie.
|
|||||||
| Restore-Test Unraid OS Flash (Stick-Boot) | Operator | Artefakt-Validierung 2026-06-05 erledigt (`ops/maintenance/check-unraid-flash-backup.sh`). **Verbleibt:** physischer Ersatzstick-Boot-Test, wenn ein Wegwerf-Stick bereitliegt | `ops/restore-tests/unraid-flash-runbook.md` |
|
| Restore-Test Unraid OS Flash (Stick-Boot) | Operator | Artefakt-Validierung 2026-06-05 erledigt (`ops/maintenance/check-unraid-flash-backup.sh`). **Verbleibt:** physischer Ersatzstick-Boot-Test, wenn ein Wegwerf-Stick bereitliegt | `ops/restore-tests/unraid-flash-runbook.md` |
|
||||||
| Restore-Test Tailscale | Operator | State-Validierung + Reconnect nur auf Wegwerf-Host/VM, danach Geraet in Tailscale-Admin entfernen | `ops/restore-tests/tailscale-runbook.md` |
|
| Restore-Test Tailscale | Operator | State-Validierung + Reconnect nur auf Wegwerf-Host/VM, danach Geraet in Tailscale-Admin entfernen | `ops/restore-tests/tailscale-runbook.md` |
|
||||||
| Authelia OIDC fuer Apps | Operator/Claude | Live: Grafana + Mealie (verifiziert), Paperless deployed (Login-Test offen). Immich + Nextcloud bewusst geparkt bis Family-Onboarding (siehe `docs/DECISIONS.md` 2026-06-06) | `docs/AUTHELIA_OIDC_PLAN.md` |
|
| Authelia OIDC fuer Apps | Operator/Claude | Live: Grafana + Mealie (verifiziert), Paperless deployed (Login-Test offen). Immich + Nextcloud bewusst geparkt bis Family-Onboarding (siehe `docs/DECISIONS.md` 2026-06-06) | `docs/AUTHELIA_OIDC_PLAN.md` |
|
||||||
|
| Glance-v2-Widgets: Tokens setzen | Operator | In Komodo Stack-ENV fuer `ops-glance` setzen: `GLANCE_KOMODO_API_KEY`/`_SECRET` (Komodo read-only API-Key), `GLANCE_GITEA_TOKEN` (read-only, scope `read:repository`), `GLANCE_PAPERLESS_TOKEN`, `GLANCE_MEALIE_TOKEN`; bis dahin zeigen die neuen Widgets Fehler/leer. Speedtest-Widget: falls weiter 0.0, API-Response pruefen | `ops/glance/config/` |
|
||||||
|
| Home Assistant Foundation-Abnahme | Operator/Codex | Sofort: Owner-Onboarding abschliessen. Danach temporaere Authelia-Middleware von der HA-Route entfernen, Komodo-Stack-Eintrag + Webhook sauber anlegen/verifizieren, HA-MQTT-Smoke-Test und HA-native `backup.create` testen, Restore-Probe fuer HA/Mosquitto dokumentieren | `docs/runbooks/smart-home-bootstrap.md`, `docs/RESTORE_MATRIX.md` |
|
||||||
|
| Audit-PDF aus `docs/` entfernen | Operator | `docs/KalliLab_CORE_Audit_2026-06-06.pdf` (untracked) extern ablegen (H:/ oder Documents-Share) und lokal loeschen; Binaerdateien gehoeren nicht ins GitOps-Repo | Doku-Regeln `docs/REPO_MAP.md` |
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
@@ -54,6 +57,7 @@ Bewusst nicht jetzt - Begruendungen in `docs/DECISIONS.md`, hier nur Thema und T
|
|||||||
| Immich Redis named volume | passende Wartung am Immich-Stack | `docs/SERVICE_CATALOG.md` |
|
| Immich Redis named volume | passende Wartung am Immich-Stack | `docs/SERVICE_CATALOG.md` |
|
||||||
| Storage-Wachstum (zweite NVMe, zweite Array-Disk, ZFS/BTRFS) | Trigger aus Capacity-Doku | `docs/STORAGE_LAYOUT.md`, `docs/CAPACITY_AND_LIFECYCLE.md` |
|
| Storage-Wachstum (zweite NVMe, zweite Array-Disk, ZFS/BTRFS) | Trigger aus Capacity-Doku | `docs/STORAGE_LAYOUT.md`, `docs/CAPACITY_AND_LIFECYCLE.md` |
|
||||||
| Wiederkehrende Restore-Drills | laufend nach Kadenz, inkl. quartalsweisem Frische-Negativtest (`run-restore-checks.sh freshness-negative`) | `docs/RESTORE_MATRIX.md`, `ops/restore-tests/schedule.md` |
|
| Wiederkehrende Restore-Drills | laufend nach Kadenz, inkl. quartalsweisem Frische-Negativtest (`run-restore-checks.sh freshness-negative`) | `docs/RESTORE_MATRIX.md`, `ops/restore-tests/schedule.md` |
|
||||||
|
| Doku-Quartals-Gaertnern (~15 min) | quartalsweise, erster Lauf mit Q3-Review ab 2026-07-01: Datiertes archivieren, Done-/Review-Logs kuerzen, tote Links pruefen | `docs/REPO_MAP.md` Doku-Regeln |
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
@@ -67,9 +71,11 @@ Bewusst nicht jetzt - Begruendungen in `docs/DECISIONS.md`, hier nur Thema und T
|
|||||||
|
|
||||||
## Zuletzt erledigt (Kurzlog, max. 5 Eintraege)
|
## Zuletzt erledigt (Kurzlog, max. 5 Eintraege)
|
||||||
|
|
||||||
- **2026-06-11** Doku-Konsolidierung umgesetzt: `docs/archive/`, `docs/DECISIONS.md`, Statuslisten auf diese Datei reduziert, Restore-Doku zusammengefuehrt. Details: `docs/DECISIONS.md` Eintrag 2026-06-11.
|
- **2026-06-12** Komodo-Stack-Hygiene-Check aktiv: `services/posture-check/komodo-stack-hygiene.sh` + Unraid User Script `komodo-stack-hygiene-weekly` (Sonntag 05:00). Faengt die `immich_new`-Klasse (Stack ohne Repo, `project_missing`, Compose ohne Stack, Hash-Drift). Erster Lauf: 6 Warnings, 0 Critical.
|
||||||
- **2026-06-06** Wochenend-Sprint abgeschlossen: Veeam-Recovery-Test, DR-Workstation-Kit final (DR-Smoke OK), Tailscale-ACL restriktiv + redundanter Docker-Stack entfernt, Gast-/IoT-Netz validiert, AdGuard-/Redis-Restore-Smokes, Authelia-2FA-Catch-all, Frische-Negativtest. Belege: Host-Reports, `docs/DECISIONS.md`.
|
- **2026-06-12** Immich Komodo-Stack bereinigt: `immich_new` auf `immich` korrigiert, Gitea-Account `Micha` gesetzt, per Komodo aus `apps/immich/docker-compose.yml` neu deployed. Verifiziert: `deployed_hash == latest_hash`, `immich_new_count=0`, alle vier Container healthy, HTTP 200, DB-Smoke `11983` Assets, Drift-Alert resolved.
|
||||||
- **2026-06-03** Restore-Backlog geschlossen: Nextcloud, Shared-PG18-Cluster, Komodo-Mongo, Mailarchiver, Mealie, Traefik. Reports unter `/mnt/user/backups/restore-reports/`.
|
- **2026-06-11** Host-DNS-Fallback aktiv: `eth0` DNS2 = `192.168.178.1` (FRITZ!Box) zusaetzlich zu AdGuard. AdGuard-SPOF fuer Image-Pulls entschaerft; der dokumentierte Bulk-Deploy-Vorfall kann strukturell nicht wiederkommen.
|
||||||
|
- **2026-06-11** Hetzner Storage Box: automatische Snapshots aktiv (taeglich 05:30 UTC, 7 Tage Retention). Schliesst das Ransomware-/Fehlbedienungs-Risiko gegen das Off-site-Backup. Siehe `docs/DECISIONS.md`.
|
||||||
|
- **2026-06-11** Immich Image-Tags von `release` auf `v2.7.5` gepinnt (Server + ML, Digests unveraendert): Renovate-PRs zeigen ab jetzt sichtbare Versionsspruenge statt stiller Digest-Bumps.
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
|||||||
@@ -14,6 +14,7 @@ geloescht (Git-Historie ist das Archiv). Verbindliche Doku-Regeln:
|
|||||||
| Datei | Zweck |
|
| Datei | Zweck |
|
||||||
|---|---|
|
|---|---|
|
||||||
| `../README.md` | kurzer Repo-Einstieg |
|
| `../README.md` | kurzer Repo-Einstieg |
|
||||||
|
| `../AGENTS.md` | Einstiegspunkt fuer KI-Agenten (Codex u. a.) |
|
||||||
| `../HOMELAB_ARCHITECTURE_MASTER_V2.md` | Architektur-Quelle fuer Netz, Zugriff und Ausnahmen |
|
| `../HOMELAB_ARCHITECTURE_MASTER_V2.md` | Architektur-Quelle fuer Netz, Zugriff und Ausnahmen |
|
||||||
| `WORKFLOW.md` | verbindlicher GitOps-/No-Drift-Ablauf |
|
| `WORKFLOW.md` | verbindlicher GitOps-/No-Drift-Ablauf |
|
||||||
| `REPO_MAP.md` | technische Landkarte des Repositories + Doku-Regeln |
|
| `REPO_MAP.md` | technische Landkarte des Repositories + Doku-Regeln |
|
||||||
|
|||||||
@@ -60,6 +60,9 @@ Sie ist die fachliche Ergaenzung zu `docs/DISASTER_RECOVERY.md`.
|
|||||||
| Glance | Git / Borg-Repo | Repo-Konfiguration unter `ops/glance/config/glance.yml`; keine kritische Datenpersistenz | keine | `GLANCE_IMMICH_API_KEY`, `GLANCE_ADGUARD_USERNAME`, `GLANCE_ADGUARD_PASSWORD`, `GLANCE_SPEEDTEST_API_KEY` | Traefik, Authelia, optional interne API-Ziele | Dashboard startet, Widgets laden, Docker-Status laeuft nur ueber `glance-docker-socket-proxy` |
|
| Glance | Git / Borg-Repo | Repo-Konfiguration unter `ops/glance/config/glance.yml`; keine kritische Datenpersistenz | keine | `GLANCE_IMMICH_API_KEY`, `GLANCE_ADGUARD_USERNAME`, `GLANCE_ADGUARD_PASSWORD`, `GLANCE_SPEEDTEST_API_KEY` | Traefik, Authelia, optional interne API-Ziele | Dashboard startet, Widgets laden, Docker-Status laeuft nur ueber `glance-docker-socket-proxy` |
|
||||||
| ntfy | Borg / Share | `/mnt/user/appdata/ntfy` | keine | keine besonderen Secret-Dateien dokumentiert | Traefik | UI und Push-Endpunkt erreichbar |
|
| ntfy | Borg / Share | `/mnt/user/appdata/ntfy` | keine | keine besonderen Secret-Dateien dokumentiert | Traefik | UI und Push-Endpunkt erreichbar |
|
||||||
| Paperless-GPT | Borg / Share | `/mnt/user/appdata/paperless-gpt` | keine eigene DB | `PAPERLESS_API_TOKEN`, `OPENAI_API_KEY` | Traefik, Paperless, OpenAI API | UI startet, Konfiguration vorhanden; LLM-Provider zeigt `openai` / `gpt-5.4-mini` |
|
| Paperless-GPT | Borg / Share | `/mnt/user/appdata/paperless-gpt` | keine eigene DB | `PAPERLESS_API_TOKEN`, `OPENAI_API_KEY` | Traefik, Paperless, OpenAI API | UI startet, Konfiguration vorhanden; LLM-Provider zeigt `openai` / `gpt-5.4-mini` |
|
||||||
|
| Home Assistant | Borg + HA-native Backups + Fachrepo | `/mnt/user/appdata/homeassistant` inkl. `.storage`, `secrets.yaml`, `trusted_proxies.yaml`; Fach-YAML aus `/mnt/user/services/smart-home-kalli/home-assistant` | HA-native Backup-Artefakte unter `/mnt/user/appdata/homeassistant/backups` falls vorhanden; keine externe DB in Phase 1 | HA-Secrets in `secrets.yaml`, Integrations-Tokens in `.storage`, MQTT-Credentials, spaeter InfluxDB-Token | Traefik, `frontend_net`, `smarthome_net`, Mosquitto, Fachrepo-Clone | `https://home.kaleschke.info` zeigt Login, MQTT-Integration verbindet sich, `backup.create` funktioniert, Energy-Dashboard-Konfiguration bleibt erhalten |
|
||||||
|
| Smart-Home MQTT / Mosquitto | Borg / Share | `/mnt/user/appdata/mosquitto/config`, `/mnt/user/appdata/mosquitto/data`, `/mnt/user/appdata/mosquitto/log` | Mosquitto persistiert retained messages/subscriptions dateibasiert | `passwordfile`, `aclfile`, spaeter per-Device-User | `smarthome_net`, Home Assistant, spaeter ESPHome/Zigbee2MQTT | Container startet, HA kann sich authentifiziert verbinden, retained Testtopic bleibt nach Restart erhalten |
|
||||||
|
| Smart-Home Fachrepo | Gitea + Borg-Repo-Clone | `/mnt/user/services/smart-home-kalli` | keine | keine echten Secrets im Repo; `secrets-template/` nur Beispiele | Gitea, Home Assistant Mounts | `git status` sauber, HA liest `configuration.yaml` und `packages/` aus dem Clone |
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
@@ -77,6 +80,8 @@ Sie ist die fachliche Ergaenzung zu `docs/DISASTER_RECOVERY.md`.
|
|||||||
| InfluxDB 3 Core | historischer Altstand / Datenuebernahme | `/mnt/user/appdata/influxdb3/data`, `/mnt/user/appdata/influxdb3/plugins` | dateibasierter Object Store | `influxdb3_admin_token.json` | `monitoring-influxdb3-core` | Datenpfad wird vom Monitoring-Zielstack weitergenutzt und darf nicht blind geloescht werden |
|
| InfluxDB 3 Core | historischer Altstand / Datenuebernahme | `/mnt/user/appdata/influxdb3/data`, `/mnt/user/appdata/influxdb3/plugins` | dateibasierter Object Store | `influxdb3_admin_token.json` | `monitoring-influxdb3-core` | Datenpfad wird vom Monitoring-Zielstack weitergenutzt und darf nicht blind geloescht werden |
|
||||||
| Loki / Alloy | historischer Altstand | `/mnt/user/appdata/loki/config`, `/mnt/user/appdata/loki/data`, `/mnt/user/appdata/alloy/config` | keine primaere DB; Loki-Dateispeicher war transient | keine zusaetzlichen Secrets | nicht aktiv | Compose-Pfad aus aktivem Repo entfernt; aktuelle Logsammlung laeuft ueber `monitoring-loki`/`monitoring-promtail` |
|
| Loki / Alloy | historischer Altstand | `/mnt/user/appdata/loki/config`, `/mnt/user/appdata/loki/data`, `/mnt/user/appdata/alloy/config` | keine primaere DB; Loki-Dateispeicher war transient | keine zusaetzlichen Secrets | nicht aktiv | Compose-Pfad aus aktivem Repo entfernt; aktuelle Logsammlung laeuft ueber `monitoring-loki`/`monitoring-promtail` |
|
||||||
| Monitoring Stack | Rebuild + named volumes + InfluxDB-Appdata | `prometheus_data`, `loki_data`, `promtail_positions`, `grafana_data`; InfluxDB unter `/mnt/user/appdata/influxdb3/data` und `/mnt/user/appdata/influxdb3/plugins`; Provisioning aus `monitoring/grafana/provisioning` | Prometheus-TSDB, Loki-Dateispeicher und InfluxDB-Dateistore; Diagnose-/Langzeitdaten, keine Tier-1-Restore-Quelle | `monitoring_grafana_admin_password.txt`, `monitoring_grafana_influxdb_token.txt`, `influxdb3_admin_token.json` | `monitoring_net`, `monitoring_influx_lan`, `frontend_net`, Traefik, Authelia, Docker socket read-only fuer Promtail, Host-Mounts fuer node-exporter/cAdvisor | `https://monitoring.kaleschke.info` leitet zu Authelia; Prometheus Targets sind up; Grafana-Datasources `Prometheus`, `Loki` und `InfluxDB 3 Core` funktionieren |
|
| Monitoring Stack | Rebuild + named volumes + InfluxDB-Appdata | `prometheus_data`, `loki_data`, `promtail_positions`, `grafana_data`; InfluxDB unter `/mnt/user/appdata/influxdb3/data` und `/mnt/user/appdata/influxdb3/plugins`; Provisioning aus `monitoring/grafana/provisioning` | Prometheus-TSDB, Loki-Dateispeicher und InfluxDB-Dateistore; Diagnose-/Langzeitdaten, keine Tier-1-Restore-Quelle | `monitoring_grafana_admin_password.txt`, `monitoring_grafana_influxdb_token.txt`, `influxdb3_admin_token.json` | `monitoring_net`, `monitoring_influx_lan`, `frontend_net`, Traefik, Authelia, Docker socket read-only fuer Promtail, Host-Mounts fuer node-exporter/cAdvisor | `https://monitoring.kaleschke.info` leitet zu Authelia; Prometheus Targets sind up; Grafana-Datasources `Prometheus`, `Loki` und `InfluxDB 3 Core` funktionieren |
|
||||||
|
| Zigbee2MQTT (geplant) | Borg + Fachrepo | `/mnt/user/appdata/zigbee2mqtt` inkl. `configuration.yaml`, `database.db`, `coordinator_backup.json`, `state.json`; Fach-Doku im Repo `smart-home-kalli` | keine externe DB | `network_key`, MQTT-Credentials, LAN-Koordinator-IP/Firmwarestand | Mosquitto, LAN-PoE-Koordinator, `smarthome_net` | Z2M startet, Coordinator verbindet sich, geraete bleiben gepairt, Testgeraet sendet MQTT-State |
|
||||||
|
| ESPHome (geplant) | Fachrepo + Borg fuer Build-/Runtime-State | `/mnt/user/appdata/esphome` falls Dashboard/Build-Cache genutzt wird; YAML unter `/mnt/user/services/smart-home-kalli/esphome` | keine | ESPHome-Secrets ausserhalb Git, API-/OTA-Keys | WLAN/LAN, Mosquitto falls MQTT genutzt wird | Dashboard startet, ein Testgeraet kompiliert/validiert, OTA/API-Verbindung funktioniert |
|
||||||
| Hermes Agent | VM-seitig offen | `/mnt/user/appdata/hermes-agent/data`, `/mnt/user/appdata/hermes-agent/ssh` | keine eigene DB | Host-`.env` fuer Provider-/API-/Home-Assistant-Tokens, `hermes_runner_id_ed25519`, `HERMES_DASHBOARD_HOST` | separate Hermes-VM/Runner, Traefik, Authelia, `hermes_net` | NAS-Stack nicht starten, solange Runner-VM und echte `.env` fehlen |
|
| Hermes Agent | VM-seitig offen | `/mnt/user/appdata/hermes-agent/data`, `/mnt/user/appdata/hermes-agent/ssh` | keine eigene DB | Host-`.env` fuer Provider-/API-/Home-Assistant-Tokens, `hermes_runner_id_ed25519`, `HERMES_DASHBOARD_HOST` | separate Hermes-VM/Runner, Traefik, Authelia, `hermes_net` | NAS-Stack nicht starten, solange Runner-VM und echte `.env` fehlen |
|
||||||
| ddns-updater | Rebuildbar | geringe Persistenzrelevanz | keine | Provider-Zugang ueber Stack ENV | Internetzugang | Update-Job laeuft |
|
| ddns-updater | Rebuildbar | geringe Persistenzrelevanz | keine | Provider-Zugang ueber Stack ENV | Internetzugang | Update-Job laeuft |
|
||||||
|
|
||||||
@@ -180,4 +185,4 @@ Verbleibende offene Restore-Pfade ohne vollstaendigen Test:
|
|||||||
Die Ablaeufe je Dienst liegen als Runbooks und automatisierte Skripte unter
|
Die Ablaeufe je Dienst liegen als Runbooks und automatisierte Skripte unter
|
||||||
`ops/restore-tests/` (Einstieg: `ops/restore-tests/README.md`). Fuer die noch
|
`ops/restore-tests/` (Einstieg: `ops/restore-tests/README.md`). Fuer die noch
|
||||||
offenen Pfade: `ops/restore-tests/unraid-flash-runbook.md` und
|
offenen Pfade: `ops/restore-tests/unraid-flash-runbook.md` und
|
||||||
`ops/restore-tests/tailscale-runbook.md`.
|
`ops/restore-tests/tailscale-runbook.md`.
|
||||||
|
|||||||
+2
-2
@@ -40,7 +40,7 @@ Dieses Dokument listet sensible Daten, deren Ablageorte und die vorgesehene Einb
|
|||||||
| Komodo Mongo | Root Password | `/mnt/user/appdata/secrets/komodo_mongo_password.txt` -> `MONGO_INITDB_ROOT_PASSWORD_FILE` | aktiv |
|
| Komodo Mongo | Root Password | `/mnt/user/appdata/secrets/komodo_mongo_password.txt` -> `MONGO_INITDB_ROOT_PASSWORD_FILE` | aktiv |
|
||||||
| Komodo Core | App Secrets | Stack ENV `${KOMODO_SECRET_KEY}`, `${KOMODO_WEBHOOK_SECRET}`, `${KOMODO_JWT_SECRET}`, `${KOMODO_MONGO_PASSWORD}`, `${KOMODO_PERIPHERY_PASSKEY}` | aktiv |
|
| Komodo Core | App Secrets | Stack ENV `${KOMODO_SECRET_KEY}`, `${KOMODO_WEBHOOK_SECRET}`, `${KOMODO_JWT_SECRET}`, `${KOMODO_MONGO_PASSWORD}`, `${KOMODO_PERIPHERY_PASSKEY}` | aktiv |
|
||||||
| Gitea Push Mirror | GitHub fine-grained PAT fuer `michaelkaleschke-spec/homelab-infra` | Gitea Repository Mirror Settings, persistent in `/mnt/user/services/gitea/data`; kein Datei-Secret im Repo | aktiv |
|
| Gitea Push Mirror | GitHub fine-grained PAT fuer `michaelkaleschke-spec/homelab-infra` | Gitea Repository Mirror Settings, persistent in `/mnt/user/services/gitea/data`; kein Datei-Secret im Repo | aktiv |
|
||||||
| Glance | Community Widget API Tokens | Stack ENV `${GLANCE_IMMICH_API_KEY}`, `${GLANCE_ADGUARD_USERNAME}`, `${GLANCE_ADGUARD_PASSWORD}`, `${GLANCE_SPEEDTEST_API_KEY}` | aktiv |
|
| Glance | Community Widget API Tokens | Stack ENV `${GLANCE_IMMICH_API_KEY}`, `${GLANCE_ADGUARD_USERNAME}`, `${GLANCE_ADGUARD_PASSWORD}`, `${GLANCE_SPEEDTEST_API_KEY}`, `${GLANCE_KOMODO_API_KEY}`, `${GLANCE_KOMODO_API_SECRET}`, `${GLANCE_GITEA_TOKEN}`, `${GLANCE_PAPERLESS_TOKEN}`, `${GLANCE_MEALIE_TOKEN}` (alle read-only anlegen) | aktiv |
|
||||||
| speedtest-tracker | App Key / Admin-Zugang | Stack ENV `${APP_KEY}`, `${ADMIN_PASSWORD}` | aktiv |
|
| speedtest-tracker | App Key / Admin-Zugang | Stack ENV `${APP_KEY}`, `${ADMIN_PASSWORD}` | aktiv |
|
||||||
| Nextcloud | Admin User | `/mnt/user/appdata/secrets/nextcloud_admin_user.txt` -> `NEXTCLOUD_ADMIN_USER_FILE` | neu |
|
| Nextcloud | Admin User | `/mnt/user/appdata/secrets/nextcloud_admin_user.txt` -> `NEXTCLOUD_ADMIN_USER_FILE` | neu |
|
||||||
| Nextcloud | Admin Password | `/mnt/user/appdata/secrets/nextcloud_admin_password.txt` -> `NEXTCLOUD_ADMIN_PASSWORD_FILE` | neu |
|
| Nextcloud | Admin Password | `/mnt/user/appdata/secrets/nextcloud_admin_password.txt` -> `NEXTCLOUD_ADMIN_PASSWORD_FILE` | neu |
|
||||||
@@ -141,7 +141,7 @@ Einige Secrets liegen bewusst nur als Komodo Stack Environment Variables vor, we
|
|||||||
| `speedtest-tracker` | `APP_KEY`, `ADMIN_PASSWORD` | Komodo-Mongo-Dump -> Vaultwarden -> externe Notiz | `APP_KEY` ist verschluesselungsrelevant; bei echtem Verlust App-State frisch initialisieren |
|
| `speedtest-tracker` | `APP_KEY`, `ADMIN_PASSWORD` | Komodo-Mongo-Dump -> Vaultwarden -> externe Notiz | `APP_KEY` ist verschluesselungsrelevant; bei echtem Verlust App-State frisch initialisieren |
|
||||||
| `komodo-core` | `KOMODO_SECRET_KEY`, `KOMODO_WEBHOOK_SECRET`, `KOMODO_JWT_SECRET`, `KOMODO_MONGO_PASSWORD`, `KOMODO_PERIPHERY_PASSKEY` | Vaultwarden -> externe Notiz (Henne-Ei: Komodo-Mongo-Dump ist hier **nicht** Restore-Quelle, weil Komodo dafuer schon laufen muesste) | siehe `docs/SERVICES_RECOVERY.md` Komodo-Bootstrap; ohne diese Werte ist der Self-Stack nicht reproduzierbar |
|
| `komodo-core` | `KOMODO_SECRET_KEY`, `KOMODO_WEBHOOK_SECRET`, `KOMODO_JWT_SECRET`, `KOMODO_MONGO_PASSWORD`, `KOMODO_PERIPHERY_PASSKEY` | Vaultwarden -> externe Notiz (Henne-Ei: Komodo-Mongo-Dump ist hier **nicht** Restore-Quelle, weil Komodo dafuer schon laufen muesste) | siehe `docs/SERVICES_RECOVERY.md` Komodo-Bootstrap; ohne diese Werte ist der Self-Stack nicht reproduzierbar |
|
||||||
| `hermes-agent` | `HERMES_DASHBOARD_HOST` plus Provider-/API-/Home-Assistant-Tokens in Host-`.env` | Vaultwarden -> externe Notiz | Stack ist aktuell geparkt (Review 2026-07-25); ohne Werte bleibt der Stack deaktiviert, kein Schaden am Rest |
|
| `hermes-agent` | `HERMES_DASHBOARD_HOST` plus Provider-/API-/Home-Assistant-Tokens in Host-`.env` | Vaultwarden -> externe Notiz | Stack ist aktuell geparkt (Review 2026-07-25); ohne Werte bleibt der Stack deaktiviert, kein Schaden am Rest |
|
||||||
| `glance` | `GLANCE_IMMICH_API_KEY`, `GLANCE_ADGUARD_USERNAME`, `GLANCE_ADGUARD_PASSWORD`, `GLANCE_SPEEDTEST_API_KEY` | Provider-UIs (Immich, AdGuard, Speedtest-Tracker) neu erzeugen | rebuildbar; Widgets bleiben leer bis Tokens neu erzeugt sind, kein kritischer Datentopf |
|
| `glance` | `GLANCE_IMMICH_API_KEY`, `GLANCE_ADGUARD_USERNAME`, `GLANCE_ADGUARD_PASSWORD`, `GLANCE_SPEEDTEST_API_KEY`, `GLANCE_KOMODO_API_KEY`, `GLANCE_KOMODO_API_SECRET`, `GLANCE_GITEA_TOKEN`, `GLANCE_PAPERLESS_TOKEN`, `GLANCE_MEALIE_TOKEN` | Provider-UIs (Immich, AdGuard, Speedtest-Tracker, Komodo, Gitea, Paperless, Mealie) neu erzeugen | rebuildbar; alle read-only; Widgets bleiben leer bis Tokens neu erzeugt sind, kein kritischer Datentopf |
|
||||||
| `n8n` | `N8N_ENCRYPTION_KEY` | Host-Secret-Datei `/mnt/user/appdata/secrets/n8n_encryption_key.txt` -> Komodo-Mongo-Dump -> Vaultwarden -> externe Notiz | Bei Verlust aller Quellen: n8n startet, aber **alle gespeicherten Credentials sind unbrauchbar** (Re-Eingabe noetig: GMX IMAP, OpenAI, Gitea PAT). Workflows bleiben strukturell erhalten. |
|
| `n8n` | `N8N_ENCRYPTION_KEY` | Host-Secret-Datei `/mnt/user/appdata/secrets/n8n_encryption_key.txt` -> Komodo-Mongo-Dump -> Vaultwarden -> externe Notiz | Bei Verlust aller Quellen: n8n startet, aber **alle gespeicherten Credentials sind unbrauchbar** (Re-Eingabe noetig: GMX IMAP, OpenAI, Gitea PAT). Workflows bleiben strukturell erhalten. |
|
||||||
|
|
||||||
### Komodo-Sonderfall
|
### Komodo-Sonderfall
|
||||||
|
|||||||
@@ -75,11 +75,18 @@ Secret-Werte sind nicht enthalten. Es werden nur Secret-Namen, Env-Key-Namen und
|
|||||||
| `monitoring-promtail` | Docker-Log-Collector fuer Monitoring-Loki | `monitoring/docker-compose.yml`, `monitoring/promtail/promtail-config.yml` | intern | Docker socket read-only, Docker json-file Logs, Loki | named volume `promtail_positions` | rebuildbar | nein | Dokumentierte Host-Observability-Ausnahme: `/var/run/docker.sock:/var/run/docker.sock:ro` und `/var/lib/docker/containers:ro`; keine Appdaten, nur Log-Discovery |
|
| `monitoring-promtail` | Docker-Log-Collector fuer Monitoring-Loki | `monitoring/docker-compose.yml`, `monitoring/promtail/promtail-config.yml` | intern | Docker socket read-only, Docker json-file Logs, Loki | named volume `promtail_positions` | rebuildbar | nein | Dokumentierte Host-Observability-Ausnahme: `/var/run/docker.sock:/var/run/docker.sock:ro` und `/var/lib/docker/containers:ro`; keine Appdaten, nur Log-Discovery |
|
||||||
| `monitoring-node-exporter` | Host-Metriken fuer Prometheus | `monitoring/docker-compose.yml` | intern `:9100` | Host `/proc`, `/sys`, `/` read-only, Prometheus | kein kritischer Zustand | rebuildbar | nein | Host-Observability-Ausnahme mit read-only Rootfs/Proc/Sys-Mounts |
|
| `monitoring-node-exporter` | Host-Metriken fuer Prometheus | `monitoring/docker-compose.yml` | intern `:9100` | Host `/proc`, `/sys`, `/` read-only, Prometheus | kein kritischer Zustand | rebuildbar | nein | Host-Observability-Ausnahme mit read-only Rootfs/Proc/Sys-Mounts |
|
||||||
| `monitoring-cadvisor` | Container-Metriken fuer Prometheus | `monitoring/docker-compose.yml` | intern `:8080` | Docker/Host read-only Mounts, Prometheus | kein kritischer Zustand | rebuildbar | nein | Host-Observability-Ausnahme fuer Container-Metriken; keine direkten Ports |
|
| `monitoring-cadvisor` | Container-Metriken fuer Prometheus | `monitoring/docker-compose.yml` | intern `:8080` | Docker/Host read-only Mounts, Prometheus | kein kritischer Zustand | rebuildbar | nein | Host-Observability-Ausnahme fuer Container-Metriken; keine direkten Ports |
|
||||||
| `monitoring-influxdb3-core` | InfluxDB 3 Core fuer Home-Assistant-/Ecowitt-Langzeitdaten | `monitoring/docker-compose.yml` | Host-Port `8181` je `INFLUXDB_BIND_IP`, keine Public URL | Monitoring-Grafana, Home Assistant Writer | `/mnt/user/appdata/influxdb3/data`, `/mnt/user/appdata/influxdb3/plugins` | Tier 3 | nein | 2026-05-31 effektiv auf `127.0.0.1:8181` gebunden, also nicht LAN-exponiert; `user: "0"` ist fuer den lokalen Object-Store-Pfad dokumentiert; uebernimmt den bisherigen InfluxDB-Daten-/Token-Katalog; `401 Unauthorized` beim Curl ohne Token ist erwarteter Reachability-Test |
|
| `monitoring-influxdb3-core` | InfluxDB 3 Core fuer Home-Assistant-/Ecowitt-Langzeitdaten | `monitoring/docker-compose.yml` | Host-Port `8181` je `INFLUXDB_BIND_IP`, keine Public URL | Monitoring-Grafana, Home Assistant Writer | `/mnt/user/appdata/influxdb3/data`, `/mnt/user/appdata/influxdb3/plugins` | Tier 3 | nein | 2026-05-31 effektiv auf `127.0.0.1:8181` gebunden, also nicht LAN-exponiert; vor dem HA-Writer muss entschieden werden, ob `INFLUXDB_BIND_IP` auf eine LAN-IP geht oder HA gezielt ein gemeinsames internes Netz mit InfluxDB bekommt. `user: "0"` ist fuer den lokalen Object-Store-Pfad dokumentiert; `401 Unauthorized` beim Curl ohne Token ist erwarteter Reachability-Test |
|
||||||
| `hermes-gateway` | Hermes Agent Gateway/API intern | `ops/hermes-agent/docker-compose.yml` | intern `8642` auf `hermes_net` | SSH Runner (VM 192.168.178.143), LLM Provider, optional Home Assistant | `/mnt/user/appdata/hermes-agent/data`, SSH key path | Tier 3, Borg/Share | nein | NAS-Stack bleibt deaktiviert, solange die separate Hermes-VM/Runner-Seite nicht wiederhergestellt ist; kein Docker-Socket |
|
| `hermes-gateway` | Hermes Agent Gateway/API intern | `ops/hermes-agent/docker-compose.yml` | intern `8642` auf `hermes_net` | SSH Runner (VM 192.168.178.143), LLM Provider, optional Home Assistant | `/mnt/user/appdata/hermes-agent/data`, SSH key path | Tier 3, Borg/Share | nein | NAS-Stack bleibt deaktiviert, solange die separate Hermes-VM/Runner-Seite nicht wiederhergestellt ist; kein Docker-Socket |
|
||||||
| `hermes-dashboard` | Hermes Dashboard | `ops/hermes-agent/docker-compose.yml` | `https://hermes.kaleschke.info` via `${HERMES_DASHBOARD_HOST}` | `hermes-gateway`, Traefik + Authelia | shared read-only data mount | Tier 3, Borg/Share | ja + Authelia | Compose-Profil `dashboard`; aktuell VM-seitig offen, nicht Teil des NAS-Finalstarts |
|
| `hermes-dashboard` | Hermes Dashboard | `ops/hermes-agent/docker-compose.yml` | `https://hermes.kaleschke.info` via `${HERMES_DASHBOARD_HOST}` | `hermes-gateway`, Traefik + Authelia | shared read-only data mount | Tier 3, Borg/Share | ja + Authelia | Compose-Profil `dashboard`; aktuell VM-seitig offen, nicht Teil des NAS-Finalstarts |
|
||||||
| `n8n` | Workflow-Automation; aktuell genutzt fuer Mail->LLM->Gitea-Issue (Inbox `Micha/mails`) | `apps/n8n/docker-compose.yml`, `apps/n8n/workflows/*.json` | `https://n8n.kaleschke.info` | Traefik (ohne pauschale Authelia, analog Komodo/Nextcloud), GMX IMAP, OpenAI API, Gitea API | `/mnt/user/appdata/n8n/data` (SQLite, Credentials, Workflows) | Tier 2, Borg + `n8n-data` (Credentials sind nur mit `N8N_ENCRYPTION_KEY` entschluesselbar) | ja, native Auth | Wegen Webhook-Endpunkten (`/webhook/*`) bewusst ohne `authelia@file`; eigene Login-/Owner-Auth bleibt Pflicht; `N8N_ENCRYPTION_KEY` ist Stack-ENV-Pflichtsecret, Verlust macht Credentials unbrauchbar. |
|
| `n8n` | Workflow-Automation; aktuell genutzt fuer Mail->LLM->Gitea-Issue (Inbox `Micha/mails`) | `apps/n8n/docker-compose.yml`, `apps/n8n/workflows/*.json` | `https://n8n.kaleschke.info` | Traefik (ohne pauschale Authelia, analog Komodo/Nextcloud), GMX IMAP, OpenAI API, Gitea API | `/mnt/user/appdata/n8n/data` (SQLite, Credentials, Workflows) | Tier 2, Borg + `n8n-data` (Credentials sind nur mit `N8N_ENCRYPTION_KEY` entschluesselbar) | ja, native Auth | Wegen Webhook-Endpunkten (`/webhook/*`) bewusst ohne `authelia@file`; eigene Login-/Owner-Auth bleibt Pflicht; `N8N_ENCRYPTION_KEY` ist Stack-ENV-Pflichtsecret, Verlust macht Credentials unbrauchbar. |
|
||||||
|
|
||||||
|
## Smart Home
|
||||||
|
|
||||||
|
| Service | Zweck | Autoritativer Pfad | URL / Zugang | Abhaengigkeiten | Datenpfade | Backup / Restore | Traefik | Besonderheiten / TODOs |
|
||||||
|
|---|---|---|---|---|---|---|---|---|
|
||||||
|
| `homeassistant` | Zentrale Smart-Home-Steuerung, Energy Dashboard, Integrations-Hub | Runtime: `smart-home/docker-compose.yml`; Fachkonfiguration: Repo `smart-home-kalli` | `https://home.kaleschke.info`; kein direkter Host-Port in Phase 1 | Traefik, `frontend_net`, `smarthome_net`, `smarthome-mosquitto`, Fachrepo unter `/mnt/user/services/smart-home-kalli` | `/mnt/user/appdata/homeassistant` inkl. `.storage`, `secrets.yaml`, `trusted_proxies.yaml`; YAML-Fachdateien read-only aus `/mnt/user/services/smart-home-kalli/home-assistant` | Tier 2, Borg + HA-native Backups; Restore-Probe Pflicht vor produktiven Energie-Automationen | ja, native HA-Auth | HA Container statt HAOS-VM; keine Add-ons, keine Supervised-Installation. `configuration.yaml` kommt aus dem Fachrepo, `.storage` wird nicht versioniert. `http.use_x_forwarded_for` und `trusted_proxies` muessen zur Traefik-Route passen. Ecowitt-HTTP bleibt Phase-2-Entscheidung wegen globalem Traefik-Redirect. |
|
||||||
|
| `smarthome-mosquitto` | MQTT-Broker fuer HA, spaeter ESPHome und Zigbee2MQTT | `smart-home/docker-compose.yml`, `smart-home/mosquitto/config/mosquitto.conf` | intern `smarthome_net:1883`; kein LAN-Port in Phase 1 | `smarthome_net`, Passwort-/ACL-Dateien in Appdata | `/mnt/user/appdata/mosquitto/config`, `/mnt/user/appdata/mosquitto/data`, `/mnt/user/appdata/mosquitto/log` | Tier 2, Borg; Passwortdatei, ACLs und persistente Broker-Daten relevant | nein | LAN-Port `1883` erst in ESPHome-Phase mit ACLs und per-Device-Usern. |
|
||||||
|
|
||||||
## Host Operations
|
## Host Operations
|
||||||
|
|
||||||
| Service | Zweck | Autoritativer Pfad | URL / Zugang | Abhaengigkeiten | Datenpfade | Backup / Restore | Traefik | Besonderheiten / TODOs |
|
| Service | Zweck | Autoritativer Pfad | URL / Zugang | Abhaengigkeiten | Datenpfade | Backup / Restore | Traefik | Besonderheiten / TODOs |
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
# Runbook: Komodo Bulk-Deploy schlaegt mit DNS `connection refused` fehl
|
# Runbook: Komodo Bulk-Deploy schlaegt mit DNS `connection refused` fehl
|
||||||
|
|
||||||
Stand: 2026-06-10 · Typ: Runbook / ADR-light · Status: Sofortmassnahme empfohlen, noch nicht umgesetzt
|
Stand: 2026-06-11 · Typ: Runbook / ADR-light · Status: **Sofortmassnahme aktiv** (Host-DNS-Fallback gesetzt 2026-06-11 bzw. frueher)
|
||||||
|
|
||||||
## Symptom
|
## Symptom
|
||||||
|
|
||||||
@@ -19,12 +19,12 @@ Der Host nutzt **AdGuard Home als einzigen Resolver** (`/etc/resolv.conf` = nur
|
|||||||
|
|
||||||
Es ist **kein** Webhook-, Auth- oder Docker-Hub-Rate-Limit-Problem: Webhooks authentifizieren sauber, `webhook_enabled=true`, Fehlerbild ist `connection refused` auf den eigenen DNS-Port direkt nach AdGuard-Recreate. Fuer den Pull-Pfad zaehlt der Docker-Daemon/Go-Resolver (iteriert ueber die `resolv.conf`-Server und springt bei Socket-Fehlern zum naechsten), nicht der glibc-Client.
|
Es ist **kein** Webhook-, Auth- oder Docker-Hub-Rate-Limit-Problem: Webhooks authentifizieren sauber, `webhook_enabled=true`, Fehlerbild ist `connection refused` auf den eigenen DNS-Port direkt nach AdGuard-Recreate. Fuer den Pull-Pfad zaehlt der Docker-Daemon/Go-Resolver (iteriert ueber die `resolv.conf`-Server und springt bei Socket-Fehlern zum naechsten), nicht der glibc-Client.
|
||||||
|
|
||||||
## Sofortmassnahme (Schicht 1)
|
## Sofortmassnahme (Schicht 1) — umgesetzt
|
||||||
|
|
||||||
Unraid -> Settings -> Network Settings -> `eth0`:
|
Unraid -> Settings -> Network Settings -> `eth0`:
|
||||||
|
|
||||||
- DNS server 1: `192.168.178.58` (AdGuard, bleibt)
|
- DNS server 1: `192.168.178.58` (AdGuard)
|
||||||
- **DNS server 2: `192.168.178.1`** (FritzBox) -> Apply
|
- **DNS server 2: `192.168.178.1`** (FritzBox) — **gesetzt und aktiv** (Operator-Bestaetigung 2026-06-11; Apply-Button erfordert Docker-/VM-Stop, der gespeicherte Wert greift bereits ohne Re-Apply)
|
||||||
|
|
||||||
Damit ueberleben Registry-Pulls einen kurzen AdGuard-Ausfall via Resolver-Failover. Im Normalbetrieb wird weiter DNS1 (AdGuard) genutzt, der Filter bleibt aktiv.
|
Damit ueberleben Registry-Pulls einen kurzen AdGuard-Ausfall via Resolver-Failover. Im Normalbetrieb wird weiter DNS1 (AdGuard) genutzt, der Filter bleibt aktiv.
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,107 @@
|
|||||||
|
# Smart-Home Bootstrap
|
||||||
|
|
||||||
|
Ziel: Den Stack `smart-home/` auf Kallilabcore initial startklar machen, ohne
|
||||||
|
Secrets oder UI-State ins Git zu schreiben.
|
||||||
|
|
||||||
|
## 1. Fachrepo auf dem Host bereitstellen
|
||||||
|
|
||||||
|
```sh
|
||||||
|
cd /mnt/user/services
|
||||||
|
git clone https://git.kaleschke.info/Micha/smart-home-kalli.git smart-home-kalli
|
||||||
|
cd smart-home-kalli
|
||||||
|
git checkout main
|
||||||
|
```
|
||||||
|
|
||||||
|
Der Home-Assistant-Container mountet daraus einzelne YAML-Dateien read-only nach
|
||||||
|
`/config`.
|
||||||
|
|
||||||
|
## 2. Home-Assistant-Appdata vorbereiten
|
||||||
|
|
||||||
|
```sh
|
||||||
|
mkdir -p /mnt/user/appdata/homeassistant
|
||||||
|
cp /mnt/user/services/smart-home-kalli/secrets-template/secrets.yaml.example \
|
||||||
|
/mnt/user/appdata/homeassistant/secrets.yaml
|
||||||
|
cp /mnt/user/services/smart-home-kalli/secrets-template/trusted_proxies.yaml.example \
|
||||||
|
/mnt/user/appdata/homeassistant/trusted_proxies.yaml
|
||||||
|
```
|
||||||
|
|
||||||
|
Danach `trusted_proxies.yaml` auf das echte Traefik-/`frontend_net`-Subnetz
|
||||||
|
anpassen:
|
||||||
|
|
||||||
|
```sh
|
||||||
|
docker network inspect frontend_net
|
||||||
|
```
|
||||||
|
|
||||||
|
## 3. Mosquitto vorbereiten
|
||||||
|
|
||||||
|
```sh
|
||||||
|
mkdir -p /mnt/user/appdata/mosquitto/config \
|
||||||
|
/mnt/user/appdata/mosquitto/data \
|
||||||
|
/mnt/user/appdata/mosquitto/log
|
||||||
|
|
||||||
|
docker run --rm -it \
|
||||||
|
-v /mnt/user/appdata/mosquitto/config:/mosquitto/external_config \
|
||||||
|
eclipse-mosquitto:2.0.22 \
|
||||||
|
mosquitto_passwd -c /mosquitto/external_config/passwordfile homeassistant
|
||||||
|
|
||||||
|
cat > /mnt/user/appdata/mosquitto/config/aclfile <<'EOF'
|
||||||
|
user homeassistant
|
||||||
|
topic readwrite #
|
||||||
|
EOF
|
||||||
|
```
|
||||||
|
|
||||||
|
Das initiale Passwort anschliessend in
|
||||||
|
`/mnt/user/appdata/homeassistant/secrets.yaml` eintragen. LAN-Port `1883` bleibt
|
||||||
|
in Phase 1 geschlossen.
|
||||||
|
|
||||||
|
## 4. Stack deployen
|
||||||
|
|
||||||
|
Komodo-Stack:
|
||||||
|
|
||||||
|
- Repo: `homelab-infra`
|
||||||
|
- Pfad: `smart-home/docker-compose.yml`
|
||||||
|
- Branch: nach Review `master`
|
||||||
|
|
||||||
|
Nach dem Start pruefen:
|
||||||
|
|
||||||
|
```sh
|
||||||
|
docker ps --filter name=homeassistant
|
||||||
|
docker ps --filter name=smarthome-mosquitto
|
||||||
|
docker logs --tail=100 homeassistant
|
||||||
|
docker logs --tail=100 smarthome-mosquitto
|
||||||
|
```
|
||||||
|
|
||||||
|
## 5. Smoke-Test
|
||||||
|
|
||||||
|
- `https://home.kaleschke.info` zeigt die Home-Assistant-Oberflaeche.
|
||||||
|
- Keine Trusted-Proxy-Fehler im HA-Log.
|
||||||
|
- MQTT-Integration verbindet sich mit Host `smarthome-mosquitto`, Port `1883`.
|
||||||
|
- HA-native Backup-Erstellung funktioniert.
|
||||||
|
|
||||||
|
## 6. Fachrepo-Update
|
||||||
|
|
||||||
|
Das Fachrepo `/mnt/user/services/smart-home-kalli` ist kein eigener
|
||||||
|
Komodo-Stack. Aenderungen wirken erst nach diesem Host-Ablauf:
|
||||||
|
|
||||||
|
```sh
|
||||||
|
cd /mnt/user/services/smart-home-kalli
|
||||||
|
git pull --ff-only origin main
|
||||||
|
docker restart homeassistant
|
||||||
|
```
|
||||||
|
|
||||||
|
Der Restart ist Pflicht, weil `configuration.yaml`, `automations.yaml`,
|
||||||
|
`scripts.yaml` und `scenes.yaml` als Einzeldateien in den Container gemountet
|
||||||
|
werden. Nach einem `git pull` kann Docker sonst noch den alten Datei-Inode sehen.
|
||||||
|
|
||||||
|
## 7. UI-Editor-Politik
|
||||||
|
|
||||||
|
`automations.yaml`, `scripts.yaml` und `scenes.yaml` sind read-only aus Git
|
||||||
|
gemountet. Der Home-Assistant-UI-Editor fuer diese Dateien ist deshalb nicht der
|
||||||
|
primaere Schreibweg. Automationen und Scripts werden in Git gepflegt; UI-State
|
||||||
|
und Integrations-State bleiben in `.storage` und werden per Borg gesichert.
|
||||||
|
|
||||||
|
## 8. Abnahmebedingung
|
||||||
|
|
||||||
|
Vor produktiven Energie-Automationen muss ein Restore-Test fuer
|
||||||
|
`/mnt/user/appdata/homeassistant`, `/mnt/user/appdata/mosquitto` und den Clone
|
||||||
|
`/mnt/user/services/smart-home-kalli` dokumentiert sein.
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
services:
|
services:
|
||||||
postgresql17:
|
postgresql17:
|
||||||
image: postgres:18.4@sha256:8ff36f3c66371cba71d20ceedccfc3de9669a68737607888c4ef0af93abe8e39
|
image: postgres:18.4@sha256:29ee7bb30d804447dc9a91fd0d74322ae1dc3a4072cc6346f70a5ed6e783b565
|
||||||
container_name: postgresql17
|
container_name: postgresql17
|
||||||
restart: unless-stopped
|
restart: unless-stopped
|
||||||
|
|
||||||
|
|||||||
@@ -48,6 +48,10 @@ The Unraid flash configuration archive is intentional as well and must be treate
|
|||||||
| Grafana | SQLite dump from `monitoring_grafana_data` + provisioned config in Git | `/local/borg-dumps`, `monitoring/grafana/provisioning`, `monitoring/grafana/dashboards` |
|
| Grafana | SQLite dump from `monitoring_grafana_data` + provisioned config in Git | `/local/borg-dumps`, `monitoring/grafana/provisioning`, `monitoring/grafana/dashboards` |
|
||||||
| Filebrowser | file-backed state dump + file data | `/local/borg-dumps`, `/local/appdata/filebrowser` |
|
| Filebrowser | file-backed state dump + file data | `/local/borg-dumps`, `/local/appdata/filebrowser` |
|
||||||
| InfluxDB 3 Core | file data | `/local/appdata/influxdb3/data`, `/local/appdata/influxdb3/plugins` |
|
| InfluxDB 3 Core | file data | `/local/appdata/influxdb3/data`, `/local/appdata/influxdb3/plugins` |
|
||||||
|
| Home Assistant | HA-native backup + file state | `/local/appdata/homeassistant`, `/local/services/smart-home-kalli` |
|
||||||
|
| Smart-Home MQTT / Mosquitto | file data | `/local/appdata/mosquitto/config`, `/local/appdata/mosquitto/data` |
|
||||||
|
| Zigbee2MQTT (planned) | file data + coordinator state | `/local/appdata/zigbee2mqtt`, `/local/services/smart-home-kalli` |
|
||||||
|
| ESPHome (planned) | Fachrepo + optional build/runtime cache | `/local/services/smart-home-kalli/esphome`, optional `/local/appdata/esphome` |
|
||||||
| Hermes Agent | file data + SSH key | `/local/appdata/hermes-agent/data`, `/local/secrets/hermes_runner_id_ed25519` |
|
| Hermes Agent | file data + SSH key | `/local/appdata/hermes-agent/data`, `/local/secrets/hermes_runner_id_ed25519` |
|
||||||
| BentoPDF | rebuildable | no critical persistence in compose |
|
| BentoPDF | rebuildable | no critical persistence in compose |
|
||||||
|
|
||||||
@@ -87,6 +91,7 @@ The live Unraid User Scripts execute repo scripts from `/mnt/user/services/homel
|
|||||||
- SQLite: `gitea`, `vaultwarden`, `speedtest-tracker`, `borg-ui`, `grafana`
|
- SQLite: `gitea`, `vaultwarden`, `speedtest-tracker`, `borg-ui`, `grafana`
|
||||||
- File-backed state: `filebrowser.bolt.dump`
|
- File-backed state: `filebrowser.bolt.dump`
|
||||||
- Unraid flash config: `unraid-flash-config.tar.gz` plus `unraid-flash-config.tar.gz.sha256`
|
- Unraid flash config: `unraid-flash-config.tar.gz` plus `unraid-flash-config.tar.gz.sha256`
|
||||||
|
- Home Assistant native backups: created by HA under `/mnt/user/appdata/homeassistant/backups` and captured as file state
|
||||||
|
|
||||||
## Explicitly Not Backed Up as Raw Live DB Files
|
## Explicitly Not Backed Up as Raw Live DB Files
|
||||||
|
|
||||||
|
|||||||
@@ -20,5 +20,9 @@
|
|||||||
/local/appdata/komodo/periphery
|
/local/appdata/komodo/periphery
|
||||||
/local/appdata/komodo/core
|
/local/appdata/komodo/core
|
||||||
/local/services/homelab-infra
|
/local/services/homelab-infra
|
||||||
|
/local/services/smart-home-kalli
|
||||||
/local/services/stacks
|
/local/services/stacks
|
||||||
/local/services/posture-check
|
/local/services/posture-check
|
||||||
|
/local/appdata/homeassistant
|
||||||
|
/local/appdata/mosquitto/config
|
||||||
|
/local/appdata/mosquitto/data
|
||||||
|
|||||||
@@ -0,0 +1,182 @@
|
|||||||
|
/* ============================================================
|
||||||
|
KalliLab "Neon Ops v2" - Glance Custom CSS
|
||||||
|
Rotierende Akzentfarben pro Widget, Gradient-Zahlen,
|
||||||
|
animierte Header-Linien, kraeftige Glows
|
||||||
|
============================================================ */
|
||||||
|
|
||||||
|
/* --- Akzentfarben rotieren ueber die Widgets --- */
|
||||||
|
.widget { --kl-accent: 205 100% 60%; }
|
||||||
|
.widget:nth-of-type(4n+2) { --kl-accent: 172 95% 48%; }
|
||||||
|
.widget:nth-of-type(4n+3) { --kl-accent: 38 100% 55%; }
|
||||||
|
.widget:nth-of-type(4n) { --kl-accent: 145 85% 50%; }
|
||||||
|
|
||||||
|
/* --- Seiten-Hintergrund: kraeftigere Farb-Glows --- */
|
||||||
|
body {
|
||||||
|
background:
|
||||||
|
radial-gradient(1300px 700px at 85% -10%, hsla(205, 100%, 55%, 0.13), transparent 60%),
|
||||||
|
radial-gradient(1000px 600px at -10% 25%, hsla(172, 95%, 45%, 0.09), transparent 55%),
|
||||||
|
radial-gradient(900px 700px at 50% 115%, hsla(38, 100%, 50%, 0.07), transparent 60%),
|
||||||
|
var(--color-background);
|
||||||
|
background-attachment: fixed;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* --- Widgets als Karten mit Akzentrand --- */
|
||||||
|
.widget {
|
||||||
|
background: linear-gradient(
|
||||||
|
160deg,
|
||||||
|
hsla(220, 30%, 100%, 0.05),
|
||||||
|
hsla(220, 30%, 100%, 0.015)
|
||||||
|
);
|
||||||
|
border: 1px solid hsl(var(--kl-accent) / 0.18);
|
||||||
|
border-radius: 14px;
|
||||||
|
padding: 14px 16px;
|
||||||
|
box-shadow:
|
||||||
|
0 10px 30px hsla(220, 60%, 3%, 0.4),
|
||||||
|
0 0 24px hsl(var(--kl-accent) / 0.06),
|
||||||
|
inset 0 1px 0 hsla(220, 40%, 90%, 0.05);
|
||||||
|
transition: border-color 0.2s ease, box-shadow 0.25s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
.widget:hover {
|
||||||
|
border-color: hsl(var(--kl-accent) / 0.55);
|
||||||
|
box-shadow:
|
||||||
|
0 12px 36px hsla(220, 60%, 3%, 0.45),
|
||||||
|
0 0 36px hsl(var(--kl-accent) / 0.16),
|
||||||
|
inset 0 1px 0 hsla(220, 40%, 90%, 0.07);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Widgets in Gruppen/Tabs nicht doppelt einrahmen */
|
||||||
|
.widget .widget {
|
||||||
|
background: none;
|
||||||
|
border: none;
|
||||||
|
border-radius: 0;
|
||||||
|
padding: 0;
|
||||||
|
box-shadow: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* --- Widget-Titel: animierte Farbverlaufs-Linie in Akzentfarbe --- */
|
||||||
|
.widget-header {
|
||||||
|
letter-spacing: 0.14em;
|
||||||
|
position: relative;
|
||||||
|
padding-bottom: 7px;
|
||||||
|
margin-bottom: 4px;
|
||||||
|
color: hsl(var(--kl-accent) / 0.85);
|
||||||
|
}
|
||||||
|
|
||||||
|
.widget-header::after {
|
||||||
|
content: "";
|
||||||
|
position: absolute;
|
||||||
|
left: 0;
|
||||||
|
bottom: 0;
|
||||||
|
width: 64px;
|
||||||
|
height: 2px;
|
||||||
|
border-radius: 2px;
|
||||||
|
background: linear-gradient(
|
||||||
|
90deg,
|
||||||
|
hsl(var(--kl-accent)),
|
||||||
|
hsl(var(--kl-accent) / 0.25),
|
||||||
|
hsl(var(--kl-accent))
|
||||||
|
);
|
||||||
|
background-size: 200% 100%;
|
||||||
|
animation: kl-shimmer 4s linear infinite;
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes kl-shimmer {
|
||||||
|
0% { background-position: 0% 0; }
|
||||||
|
100% { background-position: 200% 0; }
|
||||||
|
}
|
||||||
|
|
||||||
|
/* --- Grosse Zahlen: Gradient-Text + Glow --- */
|
||||||
|
.color-highlight.size-h2,
|
||||||
|
.color-highlight.size-h3,
|
||||||
|
.color-primary.size-h2,
|
||||||
|
.color-primary.size-h3 {
|
||||||
|
background: linear-gradient(
|
||||||
|
120deg,
|
||||||
|
hsl(var(--kl-accent)),
|
||||||
|
hsl(var(--kl-accent) / 0.55) 60%,
|
||||||
|
hsl(210, 30%, 95%)
|
||||||
|
);
|
||||||
|
-webkit-background-clip: text;
|
||||||
|
background-clip: text;
|
||||||
|
color: transparent;
|
||||||
|
filter: drop-shadow(0 0 14px hsl(var(--kl-accent) / 0.35));
|
||||||
|
}
|
||||||
|
|
||||||
|
.color-positive {
|
||||||
|
text-shadow: 0 0 16px hsla(150, 95%, 45%, 0.45);
|
||||||
|
}
|
||||||
|
|
||||||
|
.color-negative {
|
||||||
|
text-shadow: 0 0 16px hsla(350, 95%, 58%, 0.45);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* --- Status-Punkte leuchten --- */
|
||||||
|
.monitor-site-status-icon-compact,
|
||||||
|
.monitor-site-status-icon {
|
||||||
|
filter: drop-shadow(0 0 7px hsla(150, 95%, 45%, 0.55));
|
||||||
|
}
|
||||||
|
|
||||||
|
/* --- Navigation --- */
|
||||||
|
.nav-item.nav-item-current {
|
||||||
|
text-shadow: 0 0 18px hsla(212, 100%, 60%, 0.6);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* --- Suchleiste --- */
|
||||||
|
.search {
|
||||||
|
border: 1px solid hsla(212, 90%, 65%, 0.2);
|
||||||
|
border-radius: 12px;
|
||||||
|
background: hsla(220, 30%, 100%, 0.04);
|
||||||
|
transition: border-color 0.2s ease, box-shadow 0.2s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
.search:focus-within {
|
||||||
|
border-color: hsla(212, 100%, 60%, 0.55);
|
||||||
|
box-shadow: 0 0 0 3px hsla(212, 100%, 55%, 0.15), 0 0 28px hsla(212, 100%, 55%, 0.12);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* --- Server-Stats: Balken rund, gradient, glow --- */
|
||||||
|
.progress-bar {
|
||||||
|
border: none;
|
||||||
|
height: 13px;
|
||||||
|
border-radius: 999px;
|
||||||
|
background: hsla(220, 30%, 60%, 0.12);
|
||||||
|
box-shadow: inset 0 1px 3px hsla(220, 60%, 3%, 0.5);
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
.progress-value {
|
||||||
|
border-radius: 999px;
|
||||||
|
background: linear-gradient(90deg, hsl(205, 100%, 55%), hsl(172, 95%, 48%));
|
||||||
|
box-shadow: 0 0 10px hsla(205, 100%, 55%, 0.35);
|
||||||
|
}
|
||||||
|
|
||||||
|
.progress-value-notice {
|
||||||
|
background: linear-gradient(90deg, hsl(38, 100%, 55%), hsl(355, 90%, 60%));
|
||||||
|
box-shadow: 0 0 12px hsla(355, 90%, 58%, 0.45);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* --- Feinschliff --- */
|
||||||
|
::selection {
|
||||||
|
background: hsla(212, 100%, 50%, 0.35);
|
||||||
|
}
|
||||||
|
|
||||||
|
::-webkit-scrollbar {
|
||||||
|
width: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
::-webkit-scrollbar-thumb {
|
||||||
|
background: hsla(220, 30%, 50%, 0.25);
|
||||||
|
border-radius: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
::-webkit-scrollbar-thumb:hover {
|
||||||
|
background: hsla(212, 80%, 55%, 0.45);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Reduzierte Bewegung respektieren */
|
||||||
|
@media (prefers-reduced-motion: reduce) {
|
||||||
|
.widget-header::after {
|
||||||
|
animation: none;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,287 @@
|
|||||||
|
traefik:
|
||||||
|
name: Traefik
|
||||||
|
icon: https://cdn.jsdelivr.net/gh/homarr-labs/dashboard-icons/svg/traefik.svg
|
||||||
|
url: https://traefik.kaleschke.info
|
||||||
|
description: Reverse Proxy
|
||||||
|
category: core
|
||||||
|
hide: false
|
||||||
|
gitea:
|
||||||
|
name: Gitea
|
||||||
|
icon: https://cdn.jsdelivr.net/gh/homarr-labs/dashboard-icons/svg/gitea.svg
|
||||||
|
url: https://git.kaleschke.info
|
||||||
|
description: GitOps Origin
|
||||||
|
category: core
|
||||||
|
hide: false
|
||||||
|
authelia:
|
||||||
|
name: Authelia
|
||||||
|
icon: https://cdn.jsdelivr.net/gh/homarr-labs/dashboard-icons/svg/authelia.svg
|
||||||
|
url: https://auth.kaleschke.info
|
||||||
|
description: ForwardAuth
|
||||||
|
category: core
|
||||||
|
hide: false
|
||||||
|
vaultwarden:
|
||||||
|
name: Vaultwarden
|
||||||
|
icon: https://cdn.jsdelivr.net/gh/homarr-labs/dashboard-icons/svg/vaultwarden.svg
|
||||||
|
url: https://vault.kaleschke.info
|
||||||
|
description: Password Vault
|
||||||
|
category: core
|
||||||
|
hide: false
|
||||||
|
postgresql17:
|
||||||
|
name: PostgreSQL 18
|
||||||
|
icon: si:postgresql
|
||||||
|
description: Shared DB
|
||||||
|
category: core
|
||||||
|
hide: false
|
||||||
|
Redis:
|
||||||
|
name: Redis
|
||||||
|
icon: si:redis
|
||||||
|
description: Shared Cache
|
||||||
|
category: core
|
||||||
|
hide: false
|
||||||
|
adguard:
|
||||||
|
name: AdGuard
|
||||||
|
icon: https://cdn.jsdelivr.net/gh/homarr-labs/dashboard-icons/svg/adguard-home.svg
|
||||||
|
url: http://192.168.178.58:8082
|
||||||
|
description: DNS Filter
|
||||||
|
category: network
|
||||||
|
hide: false
|
||||||
|
unbound:
|
||||||
|
name: Unbound
|
||||||
|
icon: mdi:dns
|
||||||
|
description: Upstream Resolver
|
||||||
|
category: network
|
||||||
|
hide: false
|
||||||
|
ddns-updater:
|
||||||
|
name: DDNS Updater
|
||||||
|
icon: mdi:cloud-sync
|
||||||
|
description: Cloudflare DNS
|
||||||
|
category: network
|
||||||
|
hide: false
|
||||||
|
paperless-ngx:
|
||||||
|
name: Paperless-ngx
|
||||||
|
icon: https://cdn.jsdelivr.net/gh/homarr-labs/dashboard-icons/svg/paperless-ngx.svg
|
||||||
|
url: https://paperless.kaleschke.info
|
||||||
|
description: Dokumente
|
||||||
|
category: apps
|
||||||
|
hide: false
|
||||||
|
paperless-gpt:
|
||||||
|
name: Paperless-GPT
|
||||||
|
icon: mdi:robot
|
||||||
|
url: https://paperless-gpt.kaleschke.info
|
||||||
|
description: Dokumenten-KI
|
||||||
|
category: apps
|
||||||
|
hide: false
|
||||||
|
immich_server:
|
||||||
|
name: Immich
|
||||||
|
icon: https://cdn.jsdelivr.net/gh/homarr-labs/dashboard-icons/svg/immich.svg
|
||||||
|
url: https://immich.kaleschke.info
|
||||||
|
description: Fotos und Videos
|
||||||
|
category: apps
|
||||||
|
id: immich
|
||||||
|
hide: false
|
||||||
|
immich_postgres:
|
||||||
|
name: DB
|
||||||
|
parent: immich
|
||||||
|
category: apps
|
||||||
|
hide: false
|
||||||
|
immich_redis:
|
||||||
|
name: Redis
|
||||||
|
parent: immich
|
||||||
|
category: apps
|
||||||
|
hide: false
|
||||||
|
immich_machine_learning:
|
||||||
|
name: ML
|
||||||
|
parent: immich
|
||||||
|
category: apps
|
||||||
|
hide: false
|
||||||
|
mealie:
|
||||||
|
name: Mealie
|
||||||
|
icon: https://cdn.jsdelivr.net/gh/homarr-labs/dashboard-icons/svg/mealie.svg
|
||||||
|
url: https://mealie.kaleschke.info
|
||||||
|
description: Rezepte
|
||||||
|
category: apps
|
||||||
|
id: mealie
|
||||||
|
hide: false
|
||||||
|
mealie-postgres:
|
||||||
|
name: DB
|
||||||
|
parent: mealie
|
||||||
|
category: apps
|
||||||
|
hide: false
|
||||||
|
nextcloud:
|
||||||
|
name: Nextcloud
|
||||||
|
icon: https://cdn.jsdelivr.net/gh/homarr-labs/dashboard-icons/svg/nextcloud.svg
|
||||||
|
url: https://cloud.kaleschke.info
|
||||||
|
description: Dateien und Sync
|
||||||
|
category: apps
|
||||||
|
id: nextcloud
|
||||||
|
hide: false
|
||||||
|
nextcloud-postgres:
|
||||||
|
name: DB
|
||||||
|
parent: nextcloud
|
||||||
|
category: apps
|
||||||
|
hide: false
|
||||||
|
nextcloud-redis:
|
||||||
|
name: Redis
|
||||||
|
parent: nextcloud
|
||||||
|
category: apps
|
||||||
|
hide: false
|
||||||
|
mail-archiver:
|
||||||
|
name: Mail Archiver
|
||||||
|
icon: https://cdn.jsdelivr.net/gh/homarr-labs/dashboard-icons/svg/mailcow.svg
|
||||||
|
url: https://mail.kaleschke.info
|
||||||
|
description: Mail-Archiv
|
||||||
|
category: apps
|
||||||
|
hide: false
|
||||||
|
ntfy:
|
||||||
|
name: ntfy
|
||||||
|
icon: https://cdn.jsdelivr.net/gh/homarr-labs/dashboard-icons/svg/ntfy.svg
|
||||||
|
url: https://ntfy.kaleschke.info
|
||||||
|
description: Push Alerts
|
||||||
|
category: apps
|
||||||
|
hide: false
|
||||||
|
bentopdf:
|
||||||
|
name: BentoPDF
|
||||||
|
icon: mdi:file-pdf-box
|
||||||
|
url: https://pdf.kaleschke.info
|
||||||
|
description: PDF Tools
|
||||||
|
category: apps
|
||||||
|
hide: false
|
||||||
|
glance:
|
||||||
|
name: Glance
|
||||||
|
icon: https://cdn.jsdelivr.net/gh/homarr-labs/dashboard-icons/svg/glance.svg
|
||||||
|
url: https://glance.kaleschke.info
|
||||||
|
description: Homelab Uebersicht
|
||||||
|
category: ops
|
||||||
|
hide: false
|
||||||
|
glance-docker-socket-proxy:
|
||||||
|
name: Glance Socket Proxy
|
||||||
|
icon: si:docker
|
||||||
|
description: Read-only Docker API
|
||||||
|
category: ops
|
||||||
|
hide: false
|
||||||
|
monitoring-grafana:
|
||||||
|
name: Monitoring Grafana
|
||||||
|
icon: https://cdn.jsdelivr.net/gh/homarr-labs/dashboard-icons/svg/grafana.svg
|
||||||
|
url: https://monitoring.kaleschke.info
|
||||||
|
description: Observability UI
|
||||||
|
category: ops
|
||||||
|
id: monitoring
|
||||||
|
hide: false
|
||||||
|
monitoring-prometheus:
|
||||||
|
name: Prometheus
|
||||||
|
parent: monitoring
|
||||||
|
category: ops
|
||||||
|
hide: false
|
||||||
|
monitoring-loki:
|
||||||
|
name: Loki
|
||||||
|
parent: monitoring
|
||||||
|
category: ops
|
||||||
|
hide: false
|
||||||
|
monitoring-promtail:
|
||||||
|
name: Promtail
|
||||||
|
parent: monitoring
|
||||||
|
category: ops
|
||||||
|
hide: false
|
||||||
|
monitoring-alertmanager:
|
||||||
|
name: Alertmanager
|
||||||
|
parent: monitoring
|
||||||
|
category: ops
|
||||||
|
hide: false
|
||||||
|
monitoring-alertmanager-ntfy-bridge:
|
||||||
|
name: ntfy Bridge
|
||||||
|
parent: monitoring
|
||||||
|
category: ops
|
||||||
|
hide: false
|
||||||
|
monitoring-blackbox-exporter:
|
||||||
|
name: Blackbox
|
||||||
|
parent: monitoring
|
||||||
|
category: ops
|
||||||
|
hide: false
|
||||||
|
monitoring-node-exporter:
|
||||||
|
name: Node Exporter
|
||||||
|
parent: monitoring
|
||||||
|
category: ops
|
||||||
|
hide: false
|
||||||
|
monitoring-cadvisor:
|
||||||
|
name: cAdvisor
|
||||||
|
parent: monitoring
|
||||||
|
category: ops
|
||||||
|
hide: false
|
||||||
|
monitoring-influxdb3-core:
|
||||||
|
name: InfluxDB 3
|
||||||
|
parent: monitoring
|
||||||
|
category: ops
|
||||||
|
hide: false
|
||||||
|
glances:
|
||||||
|
name: Glances
|
||||||
|
icon: https://cdn.jsdelivr.net/gh/homarr-labs/dashboard-icons/svg/glances.svg
|
||||||
|
url: https://glances.kaleschke.info
|
||||||
|
description: Host-Monitoring
|
||||||
|
category: ops
|
||||||
|
hide: false
|
||||||
|
scrutiny:
|
||||||
|
name: Scrutiny
|
||||||
|
icon: https://cdn.jsdelivr.net/gh/homarr-labs/dashboard-icons/svg/scrutiny.svg
|
||||||
|
url: https://scrutiny.kaleschke.info
|
||||||
|
description: SMART
|
||||||
|
category: ops
|
||||||
|
hide: false
|
||||||
|
speedtest-tracker:
|
||||||
|
name: Speedtest
|
||||||
|
icon: https://cdn.jsdelivr.net/gh/homarr-labs/dashboard-icons/png/speedtest-tracker.png
|
||||||
|
url: https://speedtest.kaleschke.info
|
||||||
|
description: WAN-Messung
|
||||||
|
category: ops
|
||||||
|
hide: false
|
||||||
|
filebrowser:
|
||||||
|
name: Filebrowser
|
||||||
|
icon: https://cdn.jsdelivr.net/gh/homarr-labs/dashboard-icons/svg/filebrowser.svg
|
||||||
|
url: https://files.kaleschke.info
|
||||||
|
description: Dateizugriff
|
||||||
|
category: ops
|
||||||
|
hide: false
|
||||||
|
code-server:
|
||||||
|
name: code-server
|
||||||
|
icon: https://cdn.jsdelivr.net/gh/homarr-labs/dashboard-icons/svg/vscode.svg
|
||||||
|
url: https://code.kaleschke.info
|
||||||
|
description: Web IDE
|
||||||
|
category: ops
|
||||||
|
hide: false
|
||||||
|
borg-ui:
|
||||||
|
name: Borg UI
|
||||||
|
icon: mdi:archive-sync
|
||||||
|
url: https://borg.kaleschke.info
|
||||||
|
description: Backup und Restore
|
||||||
|
category: ops
|
||||||
|
hide: false
|
||||||
|
hermes-dashboard:
|
||||||
|
name: Hermes
|
||||||
|
icon: mdi:shield-sparkles
|
||||||
|
url: https://hermes.kaleschke.info
|
||||||
|
description: Ops Agent UI
|
||||||
|
category: ops
|
||||||
|
id: hermes
|
||||||
|
hide: false
|
||||||
|
hermes-gateway:
|
||||||
|
name: Gateway
|
||||||
|
parent: hermes
|
||||||
|
category: ops
|
||||||
|
hide: false
|
||||||
|
komodo-core:
|
||||||
|
name: Komodo
|
||||||
|
icon: sh:komodo
|
||||||
|
url: https://komodo.kaleschke.info
|
||||||
|
description: Stack Manager
|
||||||
|
category: ops
|
||||||
|
id: komodo
|
||||||
|
hide: false
|
||||||
|
komodo-mongo:
|
||||||
|
name: Mongo
|
||||||
|
parent: komodo
|
||||||
|
category: ops
|
||||||
|
hide: false
|
||||||
|
komodo-periphery:
|
||||||
|
name: Periphery
|
||||||
|
parent: komodo
|
||||||
|
category: ops
|
||||||
|
hide: false
|
||||||
+40
-861
@@ -1,5 +1,6 @@
|
|||||||
server:
|
server:
|
||||||
proxied: true
|
proxied: true
|
||||||
|
assets-path: /app/assets
|
||||||
|
|
||||||
branding:
|
branding:
|
||||||
app-name: KalliLab Dashboard
|
app-name: KalliLab Dashboard
|
||||||
@@ -7,867 +8,45 @@ branding:
|
|||||||
hide-footer: true
|
hide-footer: true
|
||||||
|
|
||||||
theme:
|
theme:
|
||||||
background-color: 210 20 13
|
background-color: 222 14 8
|
||||||
primary-color: 212 100 50
|
primary-color: 205 100 58
|
||||||
positive-color: 140 70 40
|
positive-color: 150 80 45
|
||||||
negative-color: 4 78 57
|
negative-color: 355 90 60
|
||||||
contrast-multiplier: 1.25
|
contrast-multiplier: 1.3
|
||||||
text-saturation-multiplier: 0.9
|
text-saturation-multiplier: 0.5
|
||||||
disable-picker: false
|
disable-picker: false
|
||||||
|
custom-css-file: /assets/custom.css
|
||||||
|
presets:
|
||||||
|
catppuccin-mocha:
|
||||||
|
background-color: 240 21 15
|
||||||
|
primary-color: 217 92 83
|
||||||
|
positive-color: 115 54 76
|
||||||
|
negative-color: 347 70 65
|
||||||
|
contrast-multiplier: 1.2
|
||||||
|
gruvbox-dark:
|
||||||
|
background-color: 0 0 16
|
||||||
|
primary-color: 43 59 81
|
||||||
|
positive-color: 61 66 44
|
||||||
|
negative-color: 6 96 59
|
||||||
|
kallilab-light:
|
||||||
|
light: true
|
||||||
|
background-color: 220 23 95
|
||||||
|
primary-color: 212 100 35
|
||||||
|
positive-color: 140 70 30
|
||||||
|
negative-color: 0 70 45
|
||||||
|
synthwave:
|
||||||
|
background-color: 265 35 10
|
||||||
|
primary-color: 320 100 65
|
||||||
|
positive-color: 175 100 50
|
||||||
|
negative-color: 0 100 65
|
||||||
|
contrast-multiplier: 1.3
|
||||||
|
matrix:
|
||||||
|
background-color: 130 25 6
|
||||||
|
primary-color: 130 100 55
|
||||||
|
positive-color: 130 100 45
|
||||||
|
negative-color: 35 100 55
|
||||||
|
contrast-multiplier: 1.25
|
||||||
|
text-saturation-multiplier: 1.2
|
||||||
|
|
||||||
pages:
|
pages:
|
||||||
- name: Home
|
$include: pages.yml
|
||||||
slug: home
|
|
||||||
width: wide
|
|
||||||
head-widgets:
|
|
||||||
- type: search
|
|
||||||
search-engine: duckduckgo
|
|
||||||
new-tab: true
|
|
||||||
autofocus: true
|
|
||||||
placeholder: Suche im Web oder springe per Bang...
|
|
||||||
bangs:
|
|
||||||
- title: Gitea
|
|
||||||
shortcut: "!git"
|
|
||||||
url: https://git.kaleschke.info/explore/repos?q={QUERY}
|
|
||||||
- title: Paperless
|
|
||||||
shortcut: "!doc"
|
|
||||||
url: https://paperless.kaleschke.info/documents?query={QUERY}
|
|
||||||
- title: Nextcloud
|
|
||||||
shortcut: "!cloud"
|
|
||||||
url: https://cloud.kaleschke.info/apps/files/?dir=/{QUERY}
|
|
||||||
- title: Komodo
|
|
||||||
shortcut: "!komodo"
|
|
||||||
url: https://komodo.kaleschke.info
|
|
||||||
columns:
|
|
||||||
- size: small
|
|
||||||
widgets:
|
|
||||||
- type: group
|
|
||||||
widgets:
|
|
||||||
- type: custom-api
|
|
||||||
title: Day
|
|
||||||
body-type: string
|
|
||||||
skip-json-validation: true
|
|
||||||
cache: 1s
|
|
||||||
template: |
|
|
||||||
{{ $localTime := now }}
|
|
||||||
{{ $elapsedSeconds := add (mul $localTime.Hour 3600) (mul $localTime.Minute 60) | add $localTime.Second }}
|
|
||||||
{{ $dayProgress := div (mul $elapsedSeconds 100.0) 86400.0 }}
|
|
||||||
{{ $gradient := "#70a1ff" }}
|
|
||||||
{{ if gt $dayProgress 25.0 }}{{ $gradient = "#ff6b6b, #70a1ff" }}{{ end }}
|
|
||||||
{{ if gt $dayProgress 50.0 }}{{ $gradient = "#ff6b6b, #f8e71c, #7ed6df" }}{{ end }}
|
|
||||||
{{ if gt $dayProgress 75.0 }}{{ $gradient = "#ff6b6b, #f8e71c, #7ed6df, #70a1ff" }}{{ end }}
|
|
||||||
<div style="text-align: center;">
|
|
||||||
<div style="width: 100%; height: 12px; background: #23262f; border: 1px solid color-mix(in srgb, var(--color-text-subdue) 55%, transparent); border-radius: 10px; overflow: hidden;">
|
|
||||||
<div style="height: 100%; width: {{ $dayProgress }}%; background: linear-gradient(90deg, {{ $gradient }});"></div>
|
|
||||||
</div>
|
|
||||||
<div class="size-h1" style="margin-top: 6px;">{{ printf "%.2f" $dayProgress }}% des Tages sind vorbei</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
- type: custom-api
|
|
||||||
title: Month
|
|
||||||
body-type: string
|
|
||||||
skip-json-validation: true
|
|
||||||
cache: 1s
|
|
||||||
template: |
|
|
||||||
{{ $localTime := now }}
|
|
||||||
{{ $month := $localTime.Month }}
|
|
||||||
{{ $daysInMonth := 31 }}
|
|
||||||
{{ if eq $month 2 }}{{ $daysInMonth = 28 }}{{ end }}
|
|
||||||
{{ if or (eq $month 4) (eq $month 6) (eq $month 9) (eq $month 11) }}{{ $daysInMonth = 30 }}{{ end }}
|
|
||||||
{{ $secondsToday := add (mul $localTime.Hour 3600) (mul $localTime.Minute 60) | add $localTime.Second }}
|
|
||||||
{{ $daysElapsed := add (sub $localTime.Day 1) (div $secondsToday 86400.0) }}
|
|
||||||
{{ $monthProgress := mul (div $daysElapsed $daysInMonth) 100.0 }}
|
|
||||||
{{ $gradient := "#70a1ff" }}
|
|
||||||
{{ if gt $monthProgress 25.0 }}{{ $gradient = "#ff6b6b, #70a1ff" }}{{ end }}
|
|
||||||
{{ if gt $monthProgress 50.0 }}{{ $gradient = "#ff6b6b, #f8e71c, #7ed6df" }}{{ end }}
|
|
||||||
{{ if gt $monthProgress 75.0 }}{{ $gradient = "#ff6b6b, #f8e71c, #7ed6df, #70a1ff" }}{{ end }}
|
|
||||||
<div style="text-align: center;">
|
|
||||||
<div style="width: 100%; height: 12px; background: #23262f; border: 1px solid color-mix(in srgb, var(--color-text-subdue) 55%, transparent); border-radius: 10px; overflow: hidden;">
|
|
||||||
<div style="height: 100%; width: {{ $monthProgress }}%; background: linear-gradient(90deg, {{ $gradient }});"></div>
|
|
||||||
</div>
|
|
||||||
<div class="size-h1" style="margin-top: 6px;">{{ printf "%.2f" $monthProgress }}% des Monats sind vorbei</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
- type: custom-api
|
|
||||||
title: Year
|
|
||||||
body-type: string
|
|
||||||
skip-json-validation: true
|
|
||||||
cache: 1s
|
|
||||||
template: |
|
|
||||||
{{ $localTime := now }}
|
|
||||||
{{ $secondsToday := add (mul $localTime.Hour 3600) (mul $localTime.Minute 60) | add $localTime.Second }}
|
|
||||||
{{ $secondsElapsed := add (mul (sub $localTime.YearDay 1) 86400) $secondsToday }}
|
|
||||||
{{ $yearProgress := div (mul $secondsElapsed 100.0) (mul 365 86400) }}
|
|
||||||
{{ $gradient := "#70a1ff" }}
|
|
||||||
{{ if gt $yearProgress 25.0 }}{{ $gradient = "#ff6b6b, #70a1ff" }}{{ end }}
|
|
||||||
{{ if gt $yearProgress 50.0 }}{{ $gradient = "#ff6b6b, #f8e71c, #7ed6df" }}{{ end }}
|
|
||||||
{{ if gt $yearProgress 75.0 }}{{ $gradient = "#ff6b6b, #f8e71c, #7ed6df, #70a1ff" }}{{ end }}
|
|
||||||
<div style="text-align: center;">
|
|
||||||
<div style="width: 100%; height: 12px; background: #23262f; border: 1px solid color-mix(in srgb, var(--color-text-subdue) 55%, transparent); border-radius: 10px; overflow: hidden;">
|
|
||||||
<div style="height: 100%; width: {{ $yearProgress }}%; background: linear-gradient(90deg, {{ $gradient }});"></div>
|
|
||||||
</div>
|
|
||||||
<div class="size-h1" style="margin-top: 6px;">{{ printf "%.2f" $yearProgress }}% des Jahres sind vorbei</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
- type: clock
|
|
||||||
hour-format: 24h
|
|
||||||
show-progress: true
|
|
||||||
timezones:
|
|
||||||
- timezone: Europe/Berlin
|
|
||||||
label: Berlin
|
|
||||||
- timezone: UTC
|
|
||||||
label: UTC
|
|
||||||
|
|
||||||
- type: calendar
|
|
||||||
first-day-of-week: monday
|
|
||||||
|
|
||||||
- type: bookmarks
|
|
||||||
title: Direkte Einstiege
|
|
||||||
groups:
|
|
||||||
- title: Core
|
|
||||||
color: 212 100 50
|
|
||||||
links:
|
|
||||||
- title: Komodo
|
|
||||||
url: https://komodo.kaleschke.info
|
|
||||||
icon: sh:komodo
|
|
||||||
- title: Gitea
|
|
||||||
url: https://git.kaleschke.info
|
|
||||||
icon: si:gitea
|
|
||||||
- title: Monitoring
|
|
||||||
url: https://monitoring.kaleschke.info
|
|
||||||
icon: si:grafana
|
|
||||||
- title: Ops
|
|
||||||
color: 45 70 55
|
|
||||||
links:
|
|
||||||
- title: Borg
|
|
||||||
url: https://borg.kaleschke.info
|
|
||||||
icon: mdi:archive
|
|
||||||
- title: Glances
|
|
||||||
url: https://glances.kaleschke.info
|
|
||||||
icon: sh:glances
|
|
||||||
- title: Scrutiny
|
|
||||||
url: https://scrutiny.kaleschke.info
|
|
||||||
icon: sh:scrutiny
|
|
||||||
|
|
||||||
- size: full
|
|
||||||
widgets:
|
|
||||||
- type: server-stats
|
|
||||||
title: Server Stats
|
|
||||||
servers:
|
|
||||||
- type: local
|
|
||||||
name: Kallilabcore
|
|
||||||
hide-mountpoints-by-default: false
|
|
||||||
|
|
||||||
- type: group
|
|
||||||
widgets:
|
|
||||||
- type: custom-api
|
|
||||||
title: Immich
|
|
||||||
title-url: https://immich.kaleschke.info
|
|
||||||
cache: 10m
|
|
||||||
url: http://immich_server:2283/api/server/statistics
|
|
||||||
headers:
|
|
||||||
x-api-key: ${GLANCE_IMMICH_API_KEY}
|
|
||||||
subrequests:
|
|
||||||
storage:
|
|
||||||
url: http://immich_server:2283/api/server/storage
|
|
||||||
headers:
|
|
||||||
x-api-key: ${GLANCE_IMMICH_API_KEY}
|
|
||||||
template: |
|
|
||||||
{{ $photos := .JSON.Int "photos" }}
|
|
||||||
{{ $videos := .JSON.Int "videos" }}
|
|
||||||
{{ $usageGiB := div (toFloat (.JSON.Int "usage")) 1073741824.0 }}
|
|
||||||
{{ $storage := .Subrequest "storage" }}
|
|
||||||
{{ $storageOK := and (ge $storage.Response.StatusCode 200) (le $storage.Response.StatusCode 299) }}
|
|
||||||
{{ $percentage := 0.0 }}
|
|
||||||
{{ if $storageOK }}{{ $percentage = $storage.JSON.Float "diskUsagePercentage" }}{{ end }}
|
|
||||||
<div class="flex justify-between text-center">
|
|
||||||
<div>
|
|
||||||
<div class="color-highlight size-h3">{{ $photos | formatNumber }}</div>
|
|
||||||
<div class="size-h6 uppercase">Fotos</div>
|
|
||||||
</div>
|
|
||||||
<div>
|
|
||||||
<div class="color-highlight size-h3">{{ $videos | formatNumber }}</div>
|
|
||||||
<div class="size-h6 uppercase">Videos</div>
|
|
||||||
</div>
|
|
||||||
<div>
|
|
||||||
<div class="color-highlight size-h3">{{ printf "%.0f" $usageGiB }} GiB</div>
|
|
||||||
<div class="size-h6 uppercase">Medien</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div style="height: 8px; margin-top: 14px; border-radius: 999px; overflow: hidden; background: color-mix(in srgb, var(--color-text-subdue) 22%, transparent);">
|
|
||||||
<div style="height: 100%; width: {{ if $storageOK }}{{ printf "%.1f" $percentage }}%{{ else }}0%{{ end }}; border-radius: 999px; background: var(--color-primary);"></div>
|
|
||||||
</div>
|
|
||||||
<div class="size-h6 color-subdue" style="margin-top: 8px;">{{ if $storageOK }}{{ printf "%.1f" $percentage }}% Speicher belegt{{ else }}Speicher API nicht verfuegbar{{ end }}</div>
|
|
||||||
|
|
||||||
- type: monitor
|
|
||||||
title: Homelab Status
|
|
||||||
cache: 1m
|
|
||||||
sites:
|
|
||||||
- title: AdGuard Home
|
|
||||||
url: http://192.168.178.58:8082
|
|
||||||
check-url: http://adguard
|
|
||||||
icon: https://cdn.jsdelivr.net/gh/homarr-labs/dashboard-icons/svg/adguard-home.svg
|
|
||||||
timeout: 5s
|
|
||||||
alt-status-codes: [200, 302, 401, 403]
|
|
||||||
- title: Authelia
|
|
||||||
url: https://auth.kaleschke.info
|
|
||||||
check-url: http://authelia:9091/api/health
|
|
||||||
icon: https://cdn.jsdelivr.net/gh/homarr-labs/dashboard-icons/svg/authelia.svg
|
|
||||||
timeout: 5s
|
|
||||||
alt-status-codes: [200, 302, 401, 403]
|
|
||||||
- title: Gitea
|
|
||||||
url: https://git.kaleschke.info
|
|
||||||
check-url: http://gitea:3000/api/healthz
|
|
||||||
icon: https://cdn.jsdelivr.net/gh/homarr-labs/dashboard-icons/svg/gitea.svg
|
|
||||||
timeout: 5s
|
|
||||||
alt-status-codes: [200, 302, 401, 403]
|
|
||||||
- title: Traefik
|
|
||||||
url: https://traefik.kaleschke.info
|
|
||||||
check-url: http://traefik:8082/metrics
|
|
||||||
icon: https://cdn.jsdelivr.net/gh/homarr-labs/dashboard-icons/svg/traefik.svg
|
|
||||||
timeout: 5s
|
|
||||||
alt-status-codes: [200, 302, 401, 403]
|
|
||||||
- title: Vaultwarden
|
|
||||||
url: https://vault.kaleschke.info
|
|
||||||
check-url: http://vaultwarden/alive
|
|
||||||
icon: https://cdn.jsdelivr.net/gh/homarr-labs/dashboard-icons/svg/vaultwarden.svg
|
|
||||||
timeout: 5s
|
|
||||||
alt-status-codes: [200, 302, 401, 403]
|
|
||||||
- title: Komodo
|
|
||||||
url: https://komodo.kaleschke.info
|
|
||||||
check-url: http://komodo-core:9120
|
|
||||||
icon: https://cdn.jsdelivr.net/gh/homarr-labs/dashboard-icons/svg/komodo.svg
|
|
||||||
timeout: 5s
|
|
||||||
alt-status-codes: [200, 302, 401, 403]
|
|
||||||
- title: Paperless-ngx
|
|
||||||
url: https://paperless.kaleschke.info
|
|
||||||
check-url: http://paperless-ngx:8000
|
|
||||||
icon: https://cdn.jsdelivr.net/gh/homarr-labs/dashboard-icons/svg/paperless-ngx.svg
|
|
||||||
timeout: 5s
|
|
||||||
alt-status-codes: [200, 302, 401, 403]
|
|
||||||
- title: Paperless-GPT
|
|
||||||
url: https://paperless-gpt.kaleschke.info
|
|
||||||
check-url: http://paperless-gpt:8080
|
|
||||||
icon: mdi:robot
|
|
||||||
timeout: 5s
|
|
||||||
alt-status-codes: [200, 302, 401, 403]
|
|
||||||
- title: Immich
|
|
||||||
url: https://immich.kaleschke.info
|
|
||||||
check-url: http://immich_server:2283/api/server/ping
|
|
||||||
icon: https://cdn.jsdelivr.net/gh/homarr-labs/dashboard-icons/svg/immich.svg
|
|
||||||
timeout: 5s
|
|
||||||
alt-status-codes: [200, 302, 401, 403]
|
|
||||||
- title: Mealie
|
|
||||||
url: https://mealie.kaleschke.info
|
|
||||||
check-url: http://mealie:9000
|
|
||||||
icon: https://cdn.jsdelivr.net/gh/homarr-labs/dashboard-icons/svg/mealie.svg
|
|
||||||
timeout: 5s
|
|
||||||
alt-status-codes: [200, 302, 401, 403]
|
|
||||||
- title: Nextcloud
|
|
||||||
url: https://cloud.kaleschke.info
|
|
||||||
check-url: http://nextcloud/status.php
|
|
||||||
icon: https://cdn.jsdelivr.net/gh/homarr-labs/dashboard-icons/svg/nextcloud.svg
|
|
||||||
timeout: 5s
|
|
||||||
alt-status-codes: [200, 302, 401, 403]
|
|
||||||
- title: ntfy
|
|
||||||
url: https://ntfy.kaleschke.info
|
|
||||||
check-url: http://ntfy/v1/health
|
|
||||||
icon: https://cdn.jsdelivr.net/gh/homarr-labs/dashboard-icons/svg/ntfy.svg
|
|
||||||
timeout: 5s
|
|
||||||
alt-status-codes: [200, 302, 401, 403]
|
|
||||||
- title: Mail Archiver
|
|
||||||
url: https://mail.kaleschke.info
|
|
||||||
check-url: http://mail-archiver:5000
|
|
||||||
icon: https://cdn.jsdelivr.net/gh/homarr-labs/dashboard-icons/svg/mailcow.svg
|
|
||||||
timeout: 5s
|
|
||||||
alt-status-codes: [200, 302, 401, 403]
|
|
||||||
- title: BentoPDF
|
|
||||||
url: https://pdf.kaleschke.info
|
|
||||||
check-url: http://bentopdf:8080
|
|
||||||
icon: mdi:file-pdf-box
|
|
||||||
timeout: 5s
|
|
||||||
alt-status-codes: [200, 302, 401, 403]
|
|
||||||
- title: Glance
|
|
||||||
url: https://glance.kaleschke.info
|
|
||||||
check-url: http://glance:8080
|
|
||||||
icon: https://cdn.jsdelivr.net/gh/homarr-labs/dashboard-icons/svg/glance.svg
|
|
||||||
timeout: 5s
|
|
||||||
alt-status-codes: [200, 302, 401, 403]
|
|
||||||
- title: Monitoring Grafana
|
|
||||||
url: https://monitoring.kaleschke.info
|
|
||||||
check-url: http://monitoring-grafana:3000/api/health
|
|
||||||
icon: https://cdn.jsdelivr.net/gh/homarr-labs/dashboard-icons/svg/grafana.svg
|
|
||||||
timeout: 5s
|
|
||||||
alt-status-codes: [200, 302, 401, 403]
|
|
||||||
- title: Glances
|
|
||||||
url: https://glances.kaleschke.info
|
|
||||||
check-url: http://glances:61208
|
|
||||||
icon: https://cdn.jsdelivr.net/gh/homarr-labs/dashboard-icons/svg/glances.svg
|
|
||||||
timeout: 5s
|
|
||||||
alt-status-codes: [200, 302, 401, 403]
|
|
||||||
- title: Scrutiny
|
|
||||||
url: https://scrutiny.kaleschke.info
|
|
||||||
check-url: http://scrutiny:8080
|
|
||||||
icon: https://cdn.jsdelivr.net/gh/homarr-labs/dashboard-icons/svg/scrutiny.svg
|
|
||||||
timeout: 5s
|
|
||||||
alt-status-codes: [200, 302, 401, 403]
|
|
||||||
- title: Speedtest Tracker
|
|
||||||
url: https://speedtest.kaleschke.info
|
|
||||||
check-url: http://speedtest-tracker
|
|
||||||
icon: https://cdn.jsdelivr.net/gh/homarr-labs/dashboard-icons/png/speedtest-tracker.png
|
|
||||||
timeout: 5s
|
|
||||||
alt-status-codes: [200, 302, 401, 403]
|
|
||||||
- title: Filebrowser
|
|
||||||
url: https://files.kaleschke.info
|
|
||||||
check-url: http://filebrowser
|
|
||||||
icon: https://cdn.jsdelivr.net/gh/homarr-labs/dashboard-icons/svg/filebrowser.svg
|
|
||||||
timeout: 5s
|
|
||||||
alt-status-codes: [200, 302, 401, 403]
|
|
||||||
- title: code-server
|
|
||||||
url: https://code.kaleschke.info
|
|
||||||
check-url: http://code-server:8443
|
|
||||||
icon: https://cdn.jsdelivr.net/gh/homarr-labs/dashboard-icons/svg/vscode.svg
|
|
||||||
timeout: 5s
|
|
||||||
alt-status-codes: [200, 302, 401, 403]
|
|
||||||
- title: Borg UI
|
|
||||||
url: https://borg.kaleschke.info
|
|
||||||
check-url: http://borg-ui:8081
|
|
||||||
icon: mdi:archive-sync
|
|
||||||
timeout: 5s
|
|
||||||
alt-status-codes: [200, 302, 401, 403]
|
|
||||||
|
|
||||||
- size: small
|
|
||||||
widgets:
|
|
||||||
- type: custom-api
|
|
||||||
title: Internet
|
|
||||||
title-url: https://speedtest.kaleschke.info
|
|
||||||
cache: 1h
|
|
||||||
url: http://speedtest-tracker/api/v1/results/latest
|
|
||||||
headers:
|
|
||||||
Authorization: Bearer ${GLANCE_SPEEDTEST_API_KEY}
|
|
||||||
Accept: application/json
|
|
||||||
template: |
|
|
||||||
{{ $ip := .JSON.String "external_ip" }}
|
|
||||||
{{ if eq $ip "" }}{{ $ip = .JSON.String "data.interface.externalIp" }}{{ end }}
|
|
||||||
{{ $isp := .JSON.String "isp" }}
|
|
||||||
{{ if eq $isp "" }}{{ $isp = .JSON.String "data.isp" }}{{ end }}
|
|
||||||
{{ $server := .JSON.String "server_name" }}
|
|
||||||
{{ if eq $server "" }}{{ $server = .JSON.String "data.server_name" }}{{ end }}
|
|
||||||
<div style="display: flex; flex-direction: column; align-items: center; gap: 6px; text-align: center;">
|
|
||||||
<div class="color-primary size-h2" style="font-weight: 700;">{{ if ne $ip "" }}{{ $ip }}{{ else }}WAN online{{ end }}</div>
|
|
||||||
<div class="size-h5 color-highlight">Speedtest Tracker</div>
|
|
||||||
<div class="size-h6 color-subdue" style="font-style: italic;">{{ if ne $isp "" }}{{ $isp }}{{ else }}{{ $server }}{{ end }}</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
- type: custom-api
|
|
||||||
title: Internet Speed
|
|
||||||
title-url: https://speedtest.kaleschke.info
|
|
||||||
cache: 1h
|
|
||||||
url: http://speedtest-tracker/api/v1/results/latest
|
|
||||||
headers:
|
|
||||||
Authorization: Bearer ${GLANCE_SPEEDTEST_API_KEY}
|
|
||||||
Accept: application/json
|
|
||||||
subrequests:
|
|
||||||
stats:
|
|
||||||
url: http://speedtest-tracker/api/v1/stats
|
|
||||||
headers:
|
|
||||||
Authorization: Bearer ${GLANCE_SPEEDTEST_API_KEY}
|
|
||||||
Accept: application/json
|
|
||||||
template: |
|
|
||||||
{{ $stats := .Subrequest "stats" }}
|
|
||||||
{{ $download := .JSON.Float "download" }}
|
|
||||||
{{ if eq $download 0.0 }}{{ $download = .JSON.Float "data.download" }}{{ end }}
|
|
||||||
{{ if eq $download 0.0 }}{{ $download = div (.JSON.Float "download_bits") 1000000.0 }}{{ end }}
|
|
||||||
{{ if eq $download 0.0 }}{{ $download = div (.JSON.Float "data.download_bits") 1000000.0 }}{{ end }}
|
|
||||||
{{ $upload := .JSON.Float "upload" }}
|
|
||||||
{{ if eq $upload 0.0 }}{{ $upload = .JSON.Float "data.upload" }}{{ end }}
|
|
||||||
{{ if eq $upload 0.0 }}{{ $upload = div (.JSON.Float "upload_bits") 1000000.0 }}{{ end }}
|
|
||||||
{{ if eq $upload 0.0 }}{{ $upload = div (.JSON.Float "data.upload_bits") 1000000.0 }}{{ end }}
|
|
||||||
{{ $ping := .JSON.Float "ping" }}
|
|
||||||
{{ if eq $ping 0.0 }}{{ $ping = .JSON.Float "data.ping" }}{{ end }}
|
|
||||||
{{ $downloadAvg := $stats.JSON.Float "avg_download" }}
|
|
||||||
{{ if eq $downloadAvg 0.0 }}{{ $downloadAvg = $stats.JSON.Float "data.download.avg" }}{{ end }}
|
|
||||||
{{ if eq $downloadAvg 0.0 }}{{ $downloadAvg = div ($stats.JSON.Float "data.download.avg_bits") 1000000.0 }}{{ end }}
|
|
||||||
{{ $uploadAvg := $stats.JSON.Float "avg_upload" }}
|
|
||||||
{{ if eq $uploadAvg 0.0 }}{{ $uploadAvg = $stats.JSON.Float "data.upload.avg" }}{{ end }}
|
|
||||||
{{ if eq $uploadAvg 0.0 }}{{ $uploadAvg = div ($stats.JSON.Float "data.upload.avg_bits") 1000000.0 }}{{ end }}
|
|
||||||
{{ $pingAvg := $stats.JSON.Float "avg_ping" }}
|
|
||||||
{{ if eq $pingAvg 0.0 }}{{ $pingAvg = $stats.JSON.Float "data.ping.avg" }}{{ end }}
|
|
||||||
{{ $downloadChange := percentChange $downloadAvg $download }}
|
|
||||||
{{ $uploadChange := percentChange $uploadAvg $upload }}
|
|
||||||
{{ $pingChange := percentChange $pingAvg $ping }}
|
|
||||||
<div class="flex justify-between text-center margin-block-3">
|
|
||||||
<div>
|
|
||||||
<div class="size-small {{ if lt $downloadChange 0.0 }}color-negative{{ else }}color-positive{{ end }}">{{ printf "%+.1f%%" $downloadChange }}</div>
|
|
||||||
<div class="color-highlight size-h3">{{ printf "%.1f" $download }}</div>
|
|
||||||
<div class="size-h6 color-subdue">DOWNLOAD</div>
|
|
||||||
</div>
|
|
||||||
<div>
|
|
||||||
<div class="size-small {{ if lt $uploadChange 0.0 }}color-negative{{ else }}color-positive{{ end }}">{{ printf "%+.1f%%" $uploadChange }}</div>
|
|
||||||
<div class="color-highlight size-h3">{{ printf "%.1f" $upload }}</div>
|
|
||||||
<div class="size-h6 color-subdue">UPLOAD</div>
|
|
||||||
</div>
|
|
||||||
<div>
|
|
||||||
<div class="size-small {{ if gt $pingChange 0.0 }}color-negative{{ else }}color-positive{{ end }}">{{ printf "%+.1f%%" $pingChange }}</div>
|
|
||||||
<div class="color-highlight size-h3">{{ printf "%.0f ms" $ping }}</div>
|
|
||||||
<div class="size-h6 color-subdue">PING</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
- type: dns-stats
|
|
||||||
title: DNS Stats
|
|
||||||
service: adguard
|
|
||||||
url: http://adguard
|
|
||||||
username: ${GLANCE_ADGUARD_USERNAME}
|
|
||||||
password: ${GLANCE_ADGUARD_PASSWORD}
|
|
||||||
|
|
||||||
- type: monitor
|
|
||||||
title: DNS und VPN
|
|
||||||
cache: 1m
|
|
||||||
sites:
|
|
||||||
- title: AdGuard Home
|
|
||||||
url: http://192.168.178.58:8082
|
|
||||||
check-url: http://adguard
|
|
||||||
icon: https://cdn.jsdelivr.net/gh/homarr-labs/dashboard-icons/svg/adguard-home.svg
|
|
||||||
timeout: 5s
|
|
||||||
alt-status-codes: [200, 302, 401, 403]
|
|
||||||
- title: Traefik
|
|
||||||
url: https://traefik.kaleschke.info
|
|
||||||
check-url: http://traefik:8082/metrics
|
|
||||||
icon: https://cdn.jsdelivr.net/gh/homarr-labs/dashboard-icons/svg/traefik.svg
|
|
||||||
timeout: 5s
|
|
||||||
alt-status-codes: [200, 302, 401, 403]
|
|
||||||
|
|
||||||
- type: docker-containers
|
|
||||||
title: Network Container
|
|
||||||
category: network
|
|
||||||
hide-by-default: true
|
|
||||||
sock-path: tcp://glance-docker-socket-proxy:2375
|
|
||||||
containers: &containers
|
|
||||||
traefik:
|
|
||||||
name: Traefik
|
|
||||||
icon: https://cdn.jsdelivr.net/gh/homarr-labs/dashboard-icons/svg/traefik.svg
|
|
||||||
url: https://traefik.kaleschke.info
|
|
||||||
description: Reverse Proxy
|
|
||||||
category: core
|
|
||||||
hide: false
|
|
||||||
gitea:
|
|
||||||
name: Gitea
|
|
||||||
icon: https://cdn.jsdelivr.net/gh/homarr-labs/dashboard-icons/svg/gitea.svg
|
|
||||||
url: https://git.kaleschke.info
|
|
||||||
description: GitOps Origin
|
|
||||||
category: core
|
|
||||||
hide: false
|
|
||||||
authelia:
|
|
||||||
name: Authelia
|
|
||||||
icon: https://cdn.jsdelivr.net/gh/homarr-labs/dashboard-icons/svg/authelia.svg
|
|
||||||
url: https://auth.kaleschke.info
|
|
||||||
description: ForwardAuth
|
|
||||||
category: core
|
|
||||||
hide: false
|
|
||||||
vaultwarden:
|
|
||||||
name: Vaultwarden
|
|
||||||
icon: https://cdn.jsdelivr.net/gh/homarr-labs/dashboard-icons/svg/vaultwarden.svg
|
|
||||||
url: https://vault.kaleschke.info
|
|
||||||
description: Password Vault
|
|
||||||
category: core
|
|
||||||
hide: false
|
|
||||||
postgresql17:
|
|
||||||
name: PostgreSQL 18
|
|
||||||
icon: si:postgresql
|
|
||||||
description: Shared DB
|
|
||||||
category: core
|
|
||||||
hide: false
|
|
||||||
Redis:
|
|
||||||
name: Redis
|
|
||||||
icon: si:redis
|
|
||||||
description: Shared Cache
|
|
||||||
category: core
|
|
||||||
hide: false
|
|
||||||
adguard:
|
|
||||||
name: AdGuard
|
|
||||||
icon: https://cdn.jsdelivr.net/gh/homarr-labs/dashboard-icons/svg/adguard-home.svg
|
|
||||||
url: http://192.168.178.58:8082
|
|
||||||
description: DNS Filter
|
|
||||||
category: network
|
|
||||||
hide: false
|
|
||||||
unbound:
|
|
||||||
name: Unbound
|
|
||||||
icon: mdi:dns
|
|
||||||
description: Upstream Resolver
|
|
||||||
category: network
|
|
||||||
hide: false
|
|
||||||
ddns-updater:
|
|
||||||
name: DDNS Updater
|
|
||||||
icon: mdi:cloud-sync
|
|
||||||
description: Cloudflare DNS
|
|
||||||
category: network
|
|
||||||
hide: false
|
|
||||||
paperless-ngx:
|
|
||||||
name: Paperless-ngx
|
|
||||||
icon: https://cdn.jsdelivr.net/gh/homarr-labs/dashboard-icons/svg/paperless-ngx.svg
|
|
||||||
url: https://paperless.kaleschke.info
|
|
||||||
description: Dokumente
|
|
||||||
category: apps
|
|
||||||
hide: false
|
|
||||||
paperless-gpt:
|
|
||||||
name: Paperless-GPT
|
|
||||||
icon: mdi:robot
|
|
||||||
url: https://paperless-gpt.kaleschke.info
|
|
||||||
description: Dokumenten-KI
|
|
||||||
category: apps
|
|
||||||
hide: false
|
|
||||||
immich_server:
|
|
||||||
name: Immich
|
|
||||||
icon: https://cdn.jsdelivr.net/gh/homarr-labs/dashboard-icons/svg/immich.svg
|
|
||||||
url: https://immich.kaleschke.info
|
|
||||||
description: Fotos und Videos
|
|
||||||
category: apps
|
|
||||||
id: immich
|
|
||||||
hide: false
|
|
||||||
immich_postgres:
|
|
||||||
name: DB
|
|
||||||
parent: immich
|
|
||||||
category: apps
|
|
||||||
hide: false
|
|
||||||
immich_redis:
|
|
||||||
name: Redis
|
|
||||||
parent: immich
|
|
||||||
category: apps
|
|
||||||
hide: false
|
|
||||||
immich_machine_learning:
|
|
||||||
name: ML
|
|
||||||
parent: immich
|
|
||||||
category: apps
|
|
||||||
hide: false
|
|
||||||
mealie:
|
|
||||||
name: Mealie
|
|
||||||
icon: https://cdn.jsdelivr.net/gh/homarr-labs/dashboard-icons/svg/mealie.svg
|
|
||||||
url: https://mealie.kaleschke.info
|
|
||||||
description: Rezepte
|
|
||||||
category: apps
|
|
||||||
id: mealie
|
|
||||||
hide: false
|
|
||||||
mealie-postgres:
|
|
||||||
name: DB
|
|
||||||
parent: mealie
|
|
||||||
category: apps
|
|
||||||
hide: false
|
|
||||||
nextcloud:
|
|
||||||
name: Nextcloud
|
|
||||||
icon: https://cdn.jsdelivr.net/gh/homarr-labs/dashboard-icons/svg/nextcloud.svg
|
|
||||||
url: https://cloud.kaleschke.info
|
|
||||||
description: Dateien und Sync
|
|
||||||
category: apps
|
|
||||||
id: nextcloud
|
|
||||||
hide: false
|
|
||||||
nextcloud-postgres:
|
|
||||||
name: DB
|
|
||||||
parent: nextcloud
|
|
||||||
category: apps
|
|
||||||
hide: false
|
|
||||||
nextcloud-redis:
|
|
||||||
name: Redis
|
|
||||||
parent: nextcloud
|
|
||||||
category: apps
|
|
||||||
hide: false
|
|
||||||
mail-archiver:
|
|
||||||
name: Mail Archiver
|
|
||||||
icon: https://cdn.jsdelivr.net/gh/homarr-labs/dashboard-icons/svg/mailcow.svg
|
|
||||||
url: https://mail.kaleschke.info
|
|
||||||
description: Mail-Archiv
|
|
||||||
category: apps
|
|
||||||
hide: false
|
|
||||||
ntfy:
|
|
||||||
name: ntfy
|
|
||||||
icon: https://cdn.jsdelivr.net/gh/homarr-labs/dashboard-icons/svg/ntfy.svg
|
|
||||||
url: https://ntfy.kaleschke.info
|
|
||||||
description: Push Alerts
|
|
||||||
category: apps
|
|
||||||
hide: false
|
|
||||||
bentopdf:
|
|
||||||
name: BentoPDF
|
|
||||||
icon: mdi:file-pdf-box
|
|
||||||
url: https://pdf.kaleschke.info
|
|
||||||
description: PDF Tools
|
|
||||||
category: apps
|
|
||||||
hide: false
|
|
||||||
glance:
|
|
||||||
name: Glance
|
|
||||||
icon: https://cdn.jsdelivr.net/gh/homarr-labs/dashboard-icons/svg/glance.svg
|
|
||||||
url: https://glance.kaleschke.info
|
|
||||||
description: Homelab Uebersicht
|
|
||||||
category: ops
|
|
||||||
hide: false
|
|
||||||
glance-docker-socket-proxy:
|
|
||||||
name: Glance Socket Proxy
|
|
||||||
icon: si:docker
|
|
||||||
description: Read-only Docker API
|
|
||||||
category: ops
|
|
||||||
hide: false
|
|
||||||
monitoring-grafana:
|
|
||||||
name: Monitoring Grafana
|
|
||||||
icon: https://cdn.jsdelivr.net/gh/homarr-labs/dashboard-icons/svg/grafana.svg
|
|
||||||
url: https://monitoring.kaleschke.info
|
|
||||||
description: Observability UI
|
|
||||||
category: ops
|
|
||||||
id: monitoring
|
|
||||||
hide: false
|
|
||||||
monitoring-prometheus:
|
|
||||||
name: Prometheus
|
|
||||||
parent: monitoring
|
|
||||||
category: ops
|
|
||||||
hide: false
|
|
||||||
monitoring-loki:
|
|
||||||
name: Loki
|
|
||||||
parent: monitoring
|
|
||||||
category: ops
|
|
||||||
hide: false
|
|
||||||
monitoring-promtail:
|
|
||||||
name: Promtail
|
|
||||||
parent: monitoring
|
|
||||||
category: ops
|
|
||||||
hide: false
|
|
||||||
monitoring-alertmanager:
|
|
||||||
name: Alertmanager
|
|
||||||
parent: monitoring
|
|
||||||
category: ops
|
|
||||||
hide: false
|
|
||||||
monitoring-alertmanager-ntfy-bridge:
|
|
||||||
name: ntfy Bridge
|
|
||||||
parent: monitoring
|
|
||||||
category: ops
|
|
||||||
hide: false
|
|
||||||
monitoring-blackbox-exporter:
|
|
||||||
name: Blackbox
|
|
||||||
parent: monitoring
|
|
||||||
category: ops
|
|
||||||
hide: false
|
|
||||||
monitoring-node-exporter:
|
|
||||||
name: Node Exporter
|
|
||||||
parent: monitoring
|
|
||||||
category: ops
|
|
||||||
hide: false
|
|
||||||
monitoring-cadvisor:
|
|
||||||
name: cAdvisor
|
|
||||||
parent: monitoring
|
|
||||||
category: ops
|
|
||||||
hide: false
|
|
||||||
monitoring-influxdb3-core:
|
|
||||||
name: InfluxDB 3
|
|
||||||
parent: monitoring
|
|
||||||
category: ops
|
|
||||||
hide: false
|
|
||||||
glances:
|
|
||||||
name: Glances
|
|
||||||
icon: https://cdn.jsdelivr.net/gh/homarr-labs/dashboard-icons/svg/glances.svg
|
|
||||||
url: https://glances.kaleschke.info
|
|
||||||
description: Host-Monitoring
|
|
||||||
category: ops
|
|
||||||
hide: false
|
|
||||||
scrutiny:
|
|
||||||
name: Scrutiny
|
|
||||||
icon: https://cdn.jsdelivr.net/gh/homarr-labs/dashboard-icons/svg/scrutiny.svg
|
|
||||||
url: https://scrutiny.kaleschke.info
|
|
||||||
description: SMART
|
|
||||||
category: ops
|
|
||||||
hide: false
|
|
||||||
speedtest-tracker:
|
|
||||||
name: Speedtest
|
|
||||||
icon: https://cdn.jsdelivr.net/gh/homarr-labs/dashboard-icons/png/speedtest-tracker.png
|
|
||||||
url: https://speedtest.kaleschke.info
|
|
||||||
description: WAN-Messung
|
|
||||||
category: ops
|
|
||||||
hide: false
|
|
||||||
filebrowser:
|
|
||||||
name: Filebrowser
|
|
||||||
icon: https://cdn.jsdelivr.net/gh/homarr-labs/dashboard-icons/svg/filebrowser.svg
|
|
||||||
url: https://files.kaleschke.info
|
|
||||||
description: Dateizugriff
|
|
||||||
category: ops
|
|
||||||
hide: false
|
|
||||||
code-server:
|
|
||||||
name: code-server
|
|
||||||
icon: https://cdn.jsdelivr.net/gh/homarr-labs/dashboard-icons/svg/vscode.svg
|
|
||||||
url: https://code.kaleschke.info
|
|
||||||
description: Web IDE
|
|
||||||
category: ops
|
|
||||||
hide: false
|
|
||||||
borg-ui:
|
|
||||||
name: Borg UI
|
|
||||||
icon: mdi:archive-sync
|
|
||||||
url: https://borg.kaleschke.info
|
|
||||||
description: Backup und Restore
|
|
||||||
category: ops
|
|
||||||
hide: false
|
|
||||||
hermes-dashboard:
|
|
||||||
name: Hermes
|
|
||||||
icon: mdi:shield-sparkles
|
|
||||||
url: https://hermes.kaleschke.info
|
|
||||||
description: Ops Agent UI
|
|
||||||
category: ops
|
|
||||||
id: hermes
|
|
||||||
hide: false
|
|
||||||
hermes-gateway:
|
|
||||||
name: Gateway
|
|
||||||
parent: hermes
|
|
||||||
category: ops
|
|
||||||
hide: false
|
|
||||||
komodo-core:
|
|
||||||
name: Komodo
|
|
||||||
icon: sh:komodo
|
|
||||||
url: https://komodo.kaleschke.info
|
|
||||||
description: Stack Manager
|
|
||||||
category: ops
|
|
||||||
id: komodo
|
|
||||||
hide: false
|
|
||||||
komodo-mongo:
|
|
||||||
name: Mongo
|
|
||||||
parent: komodo
|
|
||||||
category: ops
|
|
||||||
hide: false
|
|
||||||
komodo-periphery:
|
|
||||||
name: Periphery
|
|
||||||
parent: komodo
|
|
||||||
category: ops
|
|
||||||
hide: false
|
|
||||||
|
|
||||||
- type: docker-containers
|
|
||||||
title: App Container
|
|
||||||
category: apps
|
|
||||||
hide-by-default: true
|
|
||||||
sock-path: tcp://glance-docker-socket-proxy:2375
|
|
||||||
containers: *containers
|
|
||||||
|
|
||||||
- type: docker-containers
|
|
||||||
title: Ops Container
|
|
||||||
category: ops
|
|
||||||
hide-by-default: true
|
|
||||||
sock-path: tcp://glance-docker-socket-proxy:2375
|
|
||||||
containers: *containers
|
|
||||||
|
|
||||||
- name: Infrastructure and Media
|
|
||||||
slug: infrastructure
|
|
||||||
width: wide
|
|
||||||
columns:
|
|
||||||
- size: small
|
|
||||||
widgets:
|
|
||||||
- type: bookmarks
|
|
||||||
title: Core
|
|
||||||
groups:
|
|
||||||
- title: Control Plane
|
|
||||||
color: 212 100 50
|
|
||||||
links:
|
|
||||||
- title: Komodo
|
|
||||||
url: https://komodo.kaleschke.info
|
|
||||||
icon: https://cdn.jsdelivr.net/gh/homarr-labs/dashboard-icons/svg/komodo.svg
|
|
||||||
- title: Gitea
|
|
||||||
url: https://git.kaleschke.info
|
|
||||||
icon: https://cdn.jsdelivr.net/gh/homarr-labs/dashboard-icons/svg/gitea.svg
|
|
||||||
- title: Traefik
|
|
||||||
url: https://traefik.kaleschke.info
|
|
||||||
icon: https://cdn.jsdelivr.net/gh/homarr-labs/dashboard-icons/svg/traefik.svg
|
|
||||||
- title: Authelia
|
|
||||||
url: https://auth.kaleschke.info
|
|
||||||
icon: https://cdn.jsdelivr.net/gh/homarr-labs/dashboard-icons/svg/authelia.svg
|
|
||||||
|
|
||||||
- type: bookmarks
|
|
||||||
title: Media und Apps
|
|
||||||
groups:
|
|
||||||
- title: Apps
|
|
||||||
color: 140 70 40
|
|
||||||
links:
|
|
||||||
- title: Immich
|
|
||||||
url: https://immich.kaleschke.info
|
|
||||||
icon: https://cdn.jsdelivr.net/gh/homarr-labs/dashboard-icons/svg/immich.svg
|
|
||||||
- title: Paperless
|
|
||||||
url: https://paperless.kaleschke.info
|
|
||||||
icon: https://cdn.jsdelivr.net/gh/homarr-labs/dashboard-icons/svg/paperless-ngx.svg
|
|
||||||
- title: Nextcloud
|
|
||||||
url: https://cloud.kaleschke.info
|
|
||||||
icon: https://cdn.jsdelivr.net/gh/homarr-labs/dashboard-icons/svg/nextcloud.svg
|
|
||||||
- title: Mealie
|
|
||||||
url: https://mealie.kaleschke.info
|
|
||||||
icon: https://cdn.jsdelivr.net/gh/homarr-labs/dashboard-icons/svg/mealie.svg
|
|
||||||
|
|
||||||
- size: full
|
|
||||||
widgets:
|
|
||||||
- type: monitor
|
|
||||||
title: Platform Checks
|
|
||||||
cache: 1m
|
|
||||||
sites:
|
|
||||||
- title: Gitea
|
|
||||||
url: https://git.kaleschke.info
|
|
||||||
check-url: http://gitea:3000/api/healthz
|
|
||||||
icon: https://cdn.jsdelivr.net/gh/homarr-labs/dashboard-icons/svg/gitea.svg
|
|
||||||
timeout: 5s
|
|
||||||
alt-status-codes: [200, 302, 401, 403]
|
|
||||||
- title: Monitoring Grafana
|
|
||||||
url: https://monitoring.kaleschke.info
|
|
||||||
check-url: http://monitoring-grafana:3000/api/health
|
|
||||||
icon: https://cdn.jsdelivr.net/gh/homarr-labs/dashboard-icons/svg/grafana.svg
|
|
||||||
timeout: 5s
|
|
||||||
alt-status-codes: [200, 302, 401, 403]
|
|
||||||
- title: Glance
|
|
||||||
url: https://glance.kaleschke.info
|
|
||||||
check-url: http://glance:8080
|
|
||||||
icon: https://cdn.jsdelivr.net/gh/homarr-labs/dashboard-icons/svg/glance.svg
|
|
||||||
timeout: 5s
|
|
||||||
alt-status-codes: [200, 302, 401, 403]
|
|
||||||
- title: Immich
|
|
||||||
url: https://immich.kaleschke.info
|
|
||||||
check-url: http://immich_server:2283/api/server/ping
|
|
||||||
icon: https://cdn.jsdelivr.net/gh/homarr-labs/dashboard-icons/svg/immich.svg
|
|
||||||
timeout: 5s
|
|
||||||
alt-status-codes: [200, 302, 401, 403]
|
|
||||||
- title: Paperless-ngx
|
|
||||||
url: https://paperless.kaleschke.info
|
|
||||||
check-url: http://paperless-ngx:8000
|
|
||||||
icon: https://cdn.jsdelivr.net/gh/homarr-labs/dashboard-icons/svg/paperless-ngx.svg
|
|
||||||
timeout: 5s
|
|
||||||
alt-status-codes: [200, 302, 401, 403]
|
|
||||||
- title: Nextcloud
|
|
||||||
url: https://cloud.kaleschke.info
|
|
||||||
check-url: http://nextcloud/status.php
|
|
||||||
icon: https://cdn.jsdelivr.net/gh/homarr-labs/dashboard-icons/svg/nextcloud.svg
|
|
||||||
timeout: 5s
|
|
||||||
alt-status-codes: [200, 302, 401, 403]
|
|
||||||
|
|
||||||
- type: docker-containers
|
|
||||||
title: Core Container
|
|
||||||
category: core
|
|
||||||
hide-by-default: true
|
|
||||||
sock-path: tcp://glance-docker-socket-proxy:2375
|
|
||||||
containers: *containers
|
|
||||||
|
|
||||||
- type: docker-containers
|
|
||||||
title: App Container
|
|
||||||
category: apps
|
|
||||||
hide-by-default: true
|
|
||||||
sock-path: tcp://glance-docker-socket-proxy:2375
|
|
||||||
containers: *containers
|
|
||||||
|
|
||||||
- type: docker-containers
|
|
||||||
title: Ops Container
|
|
||||||
category: ops
|
|
||||||
hide-by-default: true
|
|
||||||
sock-path: tcp://glance-docker-socket-proxy:2375
|
|
||||||
containers: *containers
|
|
||||||
|
|
||||||
- size: small
|
|
||||||
widgets:
|
|
||||||
- type: bookmarks
|
|
||||||
title: Ops
|
|
||||||
groups:
|
|
||||||
- title: Tools
|
|
||||||
color: 4 78 57
|
|
||||||
links:
|
|
||||||
- title: Glances
|
|
||||||
url: https://glances.kaleschke.info
|
|
||||||
icon: https://cdn.jsdelivr.net/gh/homarr-labs/dashboard-icons/svg/glances.svg
|
|
||||||
- title: Scrutiny
|
|
||||||
url: https://scrutiny.kaleschke.info
|
|
||||||
icon: https://cdn.jsdelivr.net/gh/homarr-labs/dashboard-icons/svg/scrutiny.svg
|
|
||||||
- title: Speedtest
|
|
||||||
url: https://speedtest.kaleschke.info
|
|
||||||
icon: https://cdn.jsdelivr.net/gh/homarr-labs/dashboard-icons/png/speedtest-tracker.png
|
|
||||||
|
|||||||
@@ -0,0 +1,515 @@
|
|||||||
|
- name: Home
|
||||||
|
slug: home
|
||||||
|
width: wide
|
||||||
|
head-widgets:
|
||||||
|
- type: search
|
||||||
|
search-engine: duckduckgo
|
||||||
|
new-tab: true
|
||||||
|
autofocus: true
|
||||||
|
placeholder: Suche im Web oder springe per Bang...
|
||||||
|
bangs:
|
||||||
|
- title: Gitea
|
||||||
|
shortcut: "!git"
|
||||||
|
url: https://git.kaleschke.info/explore/repos?q={QUERY}
|
||||||
|
- title: Paperless
|
||||||
|
shortcut: "!doc"
|
||||||
|
url: https://paperless.kaleschke.info/documents?query={QUERY}
|
||||||
|
- title: Nextcloud
|
||||||
|
shortcut: "!cloud"
|
||||||
|
url: https://cloud.kaleschke.info/apps/files/?dir=/{QUERY}
|
||||||
|
- title: Komodo
|
||||||
|
shortcut: "!komodo"
|
||||||
|
url: https://komodo.kaleschke.info
|
||||||
|
- title: Immich
|
||||||
|
shortcut: "!foto"
|
||||||
|
url: https://immich.kaleschke.info/search?query={QUERY}
|
||||||
|
- title: Mealie
|
||||||
|
shortcut: "!rezept"
|
||||||
|
url: https://mealie.kaleschke.info/g/home/?search={QUERY}
|
||||||
|
columns:
|
||||||
|
- size: small
|
||||||
|
widgets:
|
||||||
|
- type: group
|
||||||
|
widgets:
|
||||||
|
- type: custom-api
|
||||||
|
title: Day
|
||||||
|
body-type: string
|
||||||
|
skip-json-validation: true
|
||||||
|
cache: 1s
|
||||||
|
template: |
|
||||||
|
{{ $localTime := now }}
|
||||||
|
{{ $elapsedSeconds := add (mul $localTime.Hour 3600) (mul $localTime.Minute 60) | add $localTime.Second }}
|
||||||
|
{{ $dayProgress := div (mul $elapsedSeconds 100.0) 86400.0 }}
|
||||||
|
{{ $gradient := "#70a1ff" }}
|
||||||
|
{{ if gt $dayProgress 25.0 }}{{ $gradient = "#ff6b6b, #70a1ff" }}{{ end }}
|
||||||
|
{{ if gt $dayProgress 50.0 }}{{ $gradient = "#ff6b6b, #f8e71c, #7ed6df" }}{{ end }}
|
||||||
|
{{ if gt $dayProgress 75.0 }}{{ $gradient = "#ff6b6b, #f8e71c, #7ed6df, #70a1ff" }}{{ end }}
|
||||||
|
<div style="text-align: center;">
|
||||||
|
<div style="width: 100%; height: 12px; background: #23262f; border: 1px solid color-mix(in srgb, var(--color-text-subdue) 55%, transparent); border-radius: 10px; overflow: hidden;">
|
||||||
|
<div style="height: 100%; width: {{ $dayProgress }}%; background: linear-gradient(90deg, {{ $gradient }});"></div>
|
||||||
|
</div>
|
||||||
|
<div class="size-h1" style="margin-top: 6px;">{{ printf "%.2f" $dayProgress }}% des Tages sind vorbei</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
- type: custom-api
|
||||||
|
title: Month
|
||||||
|
body-type: string
|
||||||
|
skip-json-validation: true
|
||||||
|
cache: 1s
|
||||||
|
template: |
|
||||||
|
{{ $localTime := now }}
|
||||||
|
{{ $month := $localTime.Month }}
|
||||||
|
{{ $daysInMonth := 31 }}
|
||||||
|
{{ if eq $month 2 }}{{ $daysInMonth = 28 }}{{ end }}
|
||||||
|
{{ if or (eq $month 4) (eq $month 6) (eq $month 9) (eq $month 11) }}{{ $daysInMonth = 30 }}{{ end }}
|
||||||
|
{{ $secondsToday := add (mul $localTime.Hour 3600) (mul $localTime.Minute 60) | add $localTime.Second }}
|
||||||
|
{{ $daysElapsed := add (sub $localTime.Day 1) (div $secondsToday 86400.0) }}
|
||||||
|
{{ $monthProgress := mul (div $daysElapsed $daysInMonth) 100.0 }}
|
||||||
|
{{ $gradient := "#70a1ff" }}
|
||||||
|
{{ if gt $monthProgress 25.0 }}{{ $gradient = "#ff6b6b, #70a1ff" }}{{ end }}
|
||||||
|
{{ if gt $monthProgress 50.0 }}{{ $gradient = "#ff6b6b, #f8e71c, #7ed6df" }}{{ end }}
|
||||||
|
{{ if gt $monthProgress 75.0 }}{{ $gradient = "#ff6b6b, #f8e71c, #7ed6df, #70a1ff" }}{{ end }}
|
||||||
|
<div style="text-align: center;">
|
||||||
|
<div style="width: 100%; height: 12px; background: #23262f; border: 1px solid color-mix(in srgb, var(--color-text-subdue) 55%, transparent); border-radius: 10px; overflow: hidden;">
|
||||||
|
<div style="height: 100%; width: {{ $monthProgress }}%; background: linear-gradient(90deg, {{ $gradient }});"></div>
|
||||||
|
</div>
|
||||||
|
<div class="size-h1" style="margin-top: 6px;">{{ printf "%.2f" $monthProgress }}% des Monats sind vorbei</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
- type: custom-api
|
||||||
|
title: Year
|
||||||
|
body-type: string
|
||||||
|
skip-json-validation: true
|
||||||
|
cache: 1s
|
||||||
|
template: |
|
||||||
|
{{ $localTime := now }}
|
||||||
|
{{ $secondsToday := add (mul $localTime.Hour 3600) (mul $localTime.Minute 60) | add $localTime.Second }}
|
||||||
|
{{ $secondsElapsed := add (mul (sub $localTime.YearDay 1) 86400) $secondsToday }}
|
||||||
|
{{ $yearProgress := div (mul $secondsElapsed 100.0) (mul 365 86400) }}
|
||||||
|
{{ $gradient := "#70a1ff" }}
|
||||||
|
{{ if gt $yearProgress 25.0 }}{{ $gradient = "#ff6b6b, #70a1ff" }}{{ end }}
|
||||||
|
{{ if gt $yearProgress 50.0 }}{{ $gradient = "#ff6b6b, #f8e71c, #7ed6df" }}{{ end }}
|
||||||
|
{{ if gt $yearProgress 75.0 }}{{ $gradient = "#ff6b6b, #f8e71c, #7ed6df, #70a1ff" }}{{ end }}
|
||||||
|
<div style="text-align: center;">
|
||||||
|
<div style="width: 100%; height: 12px; background: #23262f; border: 1px solid color-mix(in srgb, var(--color-text-subdue) 55%, transparent); border-radius: 10px; overflow: hidden;">
|
||||||
|
<div style="height: 100%; width: {{ $yearProgress }}%; background: linear-gradient(90deg, {{ $gradient }});"></div>
|
||||||
|
</div>
|
||||||
|
<div class="size-h1" style="margin-top: 6px;">{{ printf "%.2f" $yearProgress }}% des Jahres sind vorbei</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
- type: clock
|
||||||
|
hour-format: 24h
|
||||||
|
show-progress: true
|
||||||
|
timezones:
|
||||||
|
- timezone: Europe/Berlin
|
||||||
|
label: Berlin
|
||||||
|
- timezone: UTC
|
||||||
|
label: UTC
|
||||||
|
|
||||||
|
- type: weather
|
||||||
|
location: Berlin, Germany
|
||||||
|
units: metric
|
||||||
|
hour-format: 24h
|
||||||
|
|
||||||
|
- type: calendar
|
||||||
|
first-day-of-week: monday
|
||||||
|
|
||||||
|
- type: to-do
|
||||||
|
title: Operator-Notizen
|
||||||
|
|
||||||
|
- type: bookmarks
|
||||||
|
title: Direkte Einstiege
|
||||||
|
groups:
|
||||||
|
- title: Core
|
||||||
|
color: 212 100 50
|
||||||
|
links:
|
||||||
|
- title: Komodo
|
||||||
|
url: https://komodo.kaleschke.info
|
||||||
|
icon: sh:komodo
|
||||||
|
- title: Gitea
|
||||||
|
url: https://git.kaleschke.info
|
||||||
|
icon: si:gitea
|
||||||
|
- title: Monitoring
|
||||||
|
url: https://monitoring.kaleschke.info
|
||||||
|
icon: si:grafana
|
||||||
|
- title: Ops
|
||||||
|
color: 45 70 55
|
||||||
|
links:
|
||||||
|
- title: Borg
|
||||||
|
url: https://borg.kaleschke.info
|
||||||
|
icon: mdi:archive
|
||||||
|
- title: Glances
|
||||||
|
url: https://glances.kaleschke.info
|
||||||
|
icon: sh:glances
|
||||||
|
- title: Scrutiny
|
||||||
|
url: https://scrutiny.kaleschke.info
|
||||||
|
icon: sh:scrutiny
|
||||||
|
|
||||||
|
- size: full
|
||||||
|
widgets:
|
||||||
|
- type: server-stats
|
||||||
|
title: Server Stats
|
||||||
|
servers:
|
||||||
|
- type: local
|
||||||
|
name: Kallilabcore
|
||||||
|
hide-mountpoints-by-default: false
|
||||||
|
|
||||||
|
- type: custom-api
|
||||||
|
title: Komodo Stacks
|
||||||
|
title-url: https://komodo.kaleschke.info
|
||||||
|
cache: 2m
|
||||||
|
url: http://komodo-core:9120/read
|
||||||
|
method: POST
|
||||||
|
body-type: json
|
||||||
|
body:
|
||||||
|
type: ListStacks
|
||||||
|
params: {}
|
||||||
|
headers:
|
||||||
|
X-Api-Key: ${GLANCE_KOMODO_API_KEY}
|
||||||
|
X-Api-Secret: ${GLANCE_KOMODO_API_SECRET}
|
||||||
|
Content-Type: application/json
|
||||||
|
template: |
|
||||||
|
{{ $stacks := .JSON.Array "@this" }}
|
||||||
|
{{ $total := len $stacks }}
|
||||||
|
{{ $running := 0 }}
|
||||||
|
{{ range $stacks }}{{ if eq (.String "info.state") "running" }}{{ $running = add $running 1 }}{{ end }}{{ end }}
|
||||||
|
{{ $problems := sub $total $running }}
|
||||||
|
{{ $divider := "border-left: 1px solid hsla(220, 40%, 70%, 0.14);" }}
|
||||||
|
<div style="display: flex; text-align: center;">
|
||||||
|
<div style="flex: 1;">
|
||||||
|
<div class="color-highlight size-h3">{{ $total }}</div>
|
||||||
|
<div class="size-h6 uppercase color-subdue">Stacks</div>
|
||||||
|
</div>
|
||||||
|
<div style="flex: 1; {{ $divider }}">
|
||||||
|
<div class="color-positive size-h3">{{ $running }}</div>
|
||||||
|
<div class="size-h6 uppercase color-subdue">Running</div>
|
||||||
|
</div>
|
||||||
|
<div style="flex: 1; {{ $divider }}">
|
||||||
|
<div class="{{ if gt $problems 0 }}color-negative{{ else }}color-subdue{{ end }} size-h3">{{ $problems }}</div>
|
||||||
|
<div class="size-h6 uppercase color-subdue">Auffaellig</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div style="height: 5px; margin-top: 14px; border-radius: 999px; overflow: hidden; background: hsla(220, 30%, 60%, 0.12);">
|
||||||
|
<div style="height: 100%; width: {{ if gt $total 0 }}{{ div (mul $running 100.0) (toFloat $total) }}{{ else }}0{{ end }}%; border-radius: 999px; background: linear-gradient(90deg, hsl(150, 85%, 42%), hsl(172, 95%, 48%));"></div>
|
||||||
|
</div>
|
||||||
|
{{ if gt $problems 0 }}
|
||||||
|
<div style="display: flex; justify-content: center; gap: 8px; flex-wrap: wrap; margin-top: 12px;">
|
||||||
|
{{ range $stacks }}
|
||||||
|
{{ if ne (.String "info.state") "running" }}
|
||||||
|
<span class="size-h6" style="padding: 3px 12px; border-radius: 999px; border: 1px solid hsla(350, 90%, 60%, 0.45); background: hsla(350, 90%, 60%, 0.08); color: var(--color-negative); letter-spacing: 0.05em;">{{ .String "name" }} · {{ .String "info.state" }}</span>
|
||||||
|
{{ end }}
|
||||||
|
{{ end }}
|
||||||
|
</div>
|
||||||
|
{{ end }}
|
||||||
|
|
||||||
|
- type: custom-api
|
||||||
|
title: Immich
|
||||||
|
title-url: https://immich.kaleschke.info
|
||||||
|
cache: 10m
|
||||||
|
url: http://immich_server:2283/api/server/statistics
|
||||||
|
headers:
|
||||||
|
x-api-key: ${GLANCE_IMMICH_API_KEY}
|
||||||
|
subrequests:
|
||||||
|
storage:
|
||||||
|
url: http://immich_server:2283/api/server/storage
|
||||||
|
headers:
|
||||||
|
x-api-key: ${GLANCE_IMMICH_API_KEY}
|
||||||
|
template: |
|
||||||
|
{{ $photos := .JSON.Int "photos" }}
|
||||||
|
{{ $videos := .JSON.Int "videos" }}
|
||||||
|
{{ $usageGiB := div (toFloat (.JSON.Int "usage")) 1073741824.0 }}
|
||||||
|
{{ $storage := .Subrequest "storage" }}
|
||||||
|
{{ $storageOK := and (ge $storage.Response.StatusCode 200) (le $storage.Response.StatusCode 299) }}
|
||||||
|
{{ $percentage := 0.0 }}
|
||||||
|
{{ if $storageOK }}{{ $percentage = $storage.JSON.Float "diskUsagePercentage" }}{{ end }}
|
||||||
|
{{ $divider := "border-left: 1px solid hsla(220, 40%, 70%, 0.14);" }}
|
||||||
|
<div style="display: flex; text-align: center;">
|
||||||
|
<div style="flex: 1;">
|
||||||
|
<div class="color-highlight size-h3">{{ $photos | formatNumber }}</div>
|
||||||
|
<div class="size-h6 uppercase color-subdue">Fotos</div>
|
||||||
|
</div>
|
||||||
|
<div style="flex: 1; {{ $divider }}">
|
||||||
|
<div class="color-highlight size-h3">{{ $videos | formatNumber }}</div>
|
||||||
|
<div class="size-h6 uppercase color-subdue">Videos</div>
|
||||||
|
</div>
|
||||||
|
<div style="flex: 1; {{ $divider }}">
|
||||||
|
<div class="color-highlight size-h3">{{ printf "%.0f" $usageGiB }} GiB</div>
|
||||||
|
<div class="size-h6 uppercase color-subdue">Medien</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div style="display: flex; align-items: center; gap: 12px; margin-top: 16px;">
|
||||||
|
<div style="flex: 1; height: 5px; border-radius: 999px; overflow: hidden; background: hsla(220, 30%, 60%, 0.12);">
|
||||||
|
<div style="height: 100%; width: {{ if $storageOK }}{{ printf "%.1f" $percentage }}%{{ else }}0%{{ end }}; border-radius: 999px; background: linear-gradient(90deg, hsl(205, 100%, 55%), hsl(172, 95%, 48%));"></div>
|
||||||
|
</div>
|
||||||
|
<div class="size-h6 color-subdue" style="white-space: nowrap;">{{ if $storageOK }}{{ printf "%.1f" $percentage }}% belegt{{ else }}Speicher API n/v{{ end }}</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
- type: group
|
||||||
|
widgets:
|
||||||
|
- type: monitor
|
||||||
|
title: Core
|
||||||
|
cache: 1m
|
||||||
|
sites:
|
||||||
|
- title: AdGuard Home
|
||||||
|
url: http://192.168.178.58:8082
|
||||||
|
check-url: http://adguard
|
||||||
|
icon: https://cdn.jsdelivr.net/gh/homarr-labs/dashboard-icons/svg/adguard-home.svg
|
||||||
|
timeout: 5s
|
||||||
|
alt-status-codes: [200, 302, 401, 403]
|
||||||
|
- title: Authelia
|
||||||
|
url: https://auth.kaleschke.info
|
||||||
|
check-url: http://authelia:9091/api/health
|
||||||
|
icon: https://cdn.jsdelivr.net/gh/homarr-labs/dashboard-icons/svg/authelia.svg
|
||||||
|
timeout: 5s
|
||||||
|
alt-status-codes: [200, 302, 401, 403]
|
||||||
|
- title: Gitea
|
||||||
|
url: https://git.kaleschke.info
|
||||||
|
check-url: http://gitea:3000/api/healthz
|
||||||
|
icon: https://cdn.jsdelivr.net/gh/homarr-labs/dashboard-icons/svg/gitea.svg
|
||||||
|
timeout: 5s
|
||||||
|
alt-status-codes: [200, 302, 401, 403]
|
||||||
|
- title: Traefik
|
||||||
|
url: https://traefik.kaleschke.info
|
||||||
|
check-url: http://traefik:8082/metrics
|
||||||
|
icon: https://cdn.jsdelivr.net/gh/homarr-labs/dashboard-icons/svg/traefik.svg
|
||||||
|
timeout: 5s
|
||||||
|
alt-status-codes: [200, 302, 401, 403]
|
||||||
|
- title: Vaultwarden
|
||||||
|
url: https://vault.kaleschke.info
|
||||||
|
check-url: http://vaultwarden/alive
|
||||||
|
icon: https://cdn.jsdelivr.net/gh/homarr-labs/dashboard-icons/svg/vaultwarden.svg
|
||||||
|
timeout: 5s
|
||||||
|
alt-status-codes: [200, 302, 401, 403]
|
||||||
|
- title: Komodo
|
||||||
|
url: https://komodo.kaleschke.info
|
||||||
|
check-url: http://komodo-core:9120
|
||||||
|
icon: https://cdn.jsdelivr.net/gh/homarr-labs/dashboard-icons/svg/komodo.svg
|
||||||
|
timeout: 5s
|
||||||
|
alt-status-codes: [200, 302, 401, 403]
|
||||||
|
- title: Glance
|
||||||
|
url: https://glance.kaleschke.info
|
||||||
|
check-url: http://glance:8080
|
||||||
|
icon: https://cdn.jsdelivr.net/gh/homarr-labs/dashboard-icons/svg/glance.svg
|
||||||
|
timeout: 5s
|
||||||
|
alt-status-codes: [200, 302, 401, 403]
|
||||||
|
|
||||||
|
- type: monitor
|
||||||
|
title: Apps
|
||||||
|
cache: 1m
|
||||||
|
sites:
|
||||||
|
- title: Paperless-ngx
|
||||||
|
url: https://paperless.kaleschke.info
|
||||||
|
check-url: http://paperless-ngx:8000
|
||||||
|
icon: https://cdn.jsdelivr.net/gh/homarr-labs/dashboard-icons/svg/paperless-ngx.svg
|
||||||
|
timeout: 5s
|
||||||
|
alt-status-codes: [200, 302, 401, 403]
|
||||||
|
- title: Paperless-GPT
|
||||||
|
url: https://paperless-gpt.kaleschke.info
|
||||||
|
check-url: http://paperless-gpt:8080
|
||||||
|
icon: mdi:robot
|
||||||
|
timeout: 5s
|
||||||
|
alt-status-codes: [200, 302, 401, 403]
|
||||||
|
- title: Immich
|
||||||
|
url: https://immich.kaleschke.info
|
||||||
|
check-url: http://immich_server:2283/api/server/ping
|
||||||
|
icon: https://cdn.jsdelivr.net/gh/homarr-labs/dashboard-icons/svg/immich.svg
|
||||||
|
timeout: 5s
|
||||||
|
alt-status-codes: [200, 302, 401, 403]
|
||||||
|
- title: Mealie
|
||||||
|
url: https://mealie.kaleschke.info
|
||||||
|
check-url: http://mealie:9000
|
||||||
|
icon: https://cdn.jsdelivr.net/gh/homarr-labs/dashboard-icons/svg/mealie.svg
|
||||||
|
timeout: 5s
|
||||||
|
alt-status-codes: [200, 302, 401, 403]
|
||||||
|
- title: Nextcloud
|
||||||
|
url: https://cloud.kaleschke.info
|
||||||
|
check-url: http://nextcloud/status.php
|
||||||
|
icon: https://cdn.jsdelivr.net/gh/homarr-labs/dashboard-icons/svg/nextcloud.svg
|
||||||
|
timeout: 5s
|
||||||
|
alt-status-codes: [200, 302, 401, 403]
|
||||||
|
- title: ntfy
|
||||||
|
url: https://ntfy.kaleschke.info
|
||||||
|
check-url: http://ntfy/v1/health
|
||||||
|
icon: https://cdn.jsdelivr.net/gh/homarr-labs/dashboard-icons/svg/ntfy.svg
|
||||||
|
timeout: 5s
|
||||||
|
alt-status-codes: [200, 302, 401, 403]
|
||||||
|
- title: Mail Archiver
|
||||||
|
url: https://mail.kaleschke.info
|
||||||
|
check-url: http://mail-archiver:5000
|
||||||
|
icon: https://cdn.jsdelivr.net/gh/homarr-labs/dashboard-icons/svg/mailcow.svg
|
||||||
|
timeout: 5s
|
||||||
|
alt-status-codes: [200, 302, 401, 403]
|
||||||
|
- title: BentoPDF
|
||||||
|
url: https://pdf.kaleschke.info
|
||||||
|
check-url: http://bentopdf:8080
|
||||||
|
icon: mdi:file-pdf-box
|
||||||
|
timeout: 5s
|
||||||
|
alt-status-codes: [200, 302, 401, 403]
|
||||||
|
|
||||||
|
- type: monitor
|
||||||
|
title: Ops
|
||||||
|
cache: 1m
|
||||||
|
sites:
|
||||||
|
- title: Monitoring Grafana
|
||||||
|
url: https://monitoring.kaleschke.info
|
||||||
|
check-url: http://monitoring-grafana:3000/api/health
|
||||||
|
icon: https://cdn.jsdelivr.net/gh/homarr-labs/dashboard-icons/svg/grafana.svg
|
||||||
|
timeout: 5s
|
||||||
|
alt-status-codes: [200, 302, 401, 403]
|
||||||
|
- title: Glances
|
||||||
|
url: https://glances.kaleschke.info
|
||||||
|
check-url: http://glances:61208
|
||||||
|
icon: https://cdn.jsdelivr.net/gh/homarr-labs/dashboard-icons/svg/glances.svg
|
||||||
|
timeout: 5s
|
||||||
|
alt-status-codes: [200, 302, 401, 403]
|
||||||
|
- title: Scrutiny
|
||||||
|
url: https://scrutiny.kaleschke.info
|
||||||
|
check-url: http://scrutiny:8080
|
||||||
|
icon: https://cdn.jsdelivr.net/gh/homarr-labs/dashboard-icons/svg/scrutiny.svg
|
||||||
|
timeout: 5s
|
||||||
|
alt-status-codes: [200, 302, 401, 403]
|
||||||
|
- title: Speedtest Tracker
|
||||||
|
url: https://speedtest.kaleschke.info
|
||||||
|
check-url: http://speedtest-tracker
|
||||||
|
icon: https://cdn.jsdelivr.net/gh/homarr-labs/dashboard-icons/png/speedtest-tracker.png
|
||||||
|
timeout: 5s
|
||||||
|
alt-status-codes: [200, 302, 401, 403]
|
||||||
|
- title: Filebrowser
|
||||||
|
url: https://files.kaleschke.info
|
||||||
|
check-url: http://filebrowser
|
||||||
|
icon: https://cdn.jsdelivr.net/gh/homarr-labs/dashboard-icons/svg/filebrowser.svg
|
||||||
|
timeout: 5s
|
||||||
|
alt-status-codes: [200, 302, 401, 403]
|
||||||
|
- title: code-server
|
||||||
|
url: https://code.kaleschke.info
|
||||||
|
check-url: http://code-server:8443
|
||||||
|
icon: https://cdn.jsdelivr.net/gh/homarr-labs/dashboard-icons/svg/vscode.svg
|
||||||
|
timeout: 5s
|
||||||
|
alt-status-codes: [200, 302, 401, 403]
|
||||||
|
- title: Borg UI
|
||||||
|
url: https://borg.kaleschke.info
|
||||||
|
check-url: http://borg-ui:8081
|
||||||
|
icon: mdi:archive-sync
|
||||||
|
timeout: 5s
|
||||||
|
alt-status-codes: [200, 302, 401, 403]
|
||||||
|
|
||||||
|
- size: small
|
||||||
|
widgets:
|
||||||
|
- type: custom-api
|
||||||
|
title: Internet
|
||||||
|
title-url: https://speedtest.kaleschke.info
|
||||||
|
cache: 1h
|
||||||
|
url: http://speedtest-tracker/api/v1/results/latest
|
||||||
|
headers:
|
||||||
|
Authorization: Bearer ${GLANCE_SPEEDTEST_API_KEY}
|
||||||
|
Accept: application/json
|
||||||
|
subrequests:
|
||||||
|
stats:
|
||||||
|
url: http://speedtest-tracker/api/v1/stats
|
||||||
|
headers:
|
||||||
|
Authorization: Bearer ${GLANCE_SPEEDTEST_API_KEY}
|
||||||
|
Accept: application/json
|
||||||
|
template: |
|
||||||
|
{{ $ip := .JSON.String "external_ip" }}
|
||||||
|
{{ if eq $ip "" }}{{ $ip = .JSON.String "data.interface.externalIp" }}{{ end }}
|
||||||
|
{{ if eq $ip "" }}{{ $ip = .JSON.String "data.data.interface.externalIp" }}{{ end }}
|
||||||
|
{{ $isp := .JSON.String "isp" }}
|
||||||
|
{{ if eq $isp "" }}{{ $isp = .JSON.String "data.isp" }}{{ end }}
|
||||||
|
{{ if eq $isp "" }}{{ $isp = .JSON.String "data.data.isp" }}{{ end }}
|
||||||
|
{{ $download := .JSON.Float "download" }}
|
||||||
|
{{ if eq $download 0.0 }}{{ $download = .JSON.Float "data.download" }}{{ end }}
|
||||||
|
{{ if eq $download 0.0 }}{{ $download = div (.JSON.Float "download_bits") 1000000.0 }}{{ end }}
|
||||||
|
{{ if eq $download 0.0 }}{{ $download = div (.JSON.Float "data.download_bits") 1000000.0 }}{{ end }}
|
||||||
|
{{ if eq $download 0.0 }}{{ $download = div (mul (.JSON.Float "data.data.download.bandwidth") 8.0) 1000000.0 }}{{ end }}
|
||||||
|
{{ $upload := .JSON.Float "upload" }}
|
||||||
|
{{ if eq $upload 0.0 }}{{ $upload = .JSON.Float "data.upload" }}{{ end }}
|
||||||
|
{{ if eq $upload 0.0 }}{{ $upload = div (.JSON.Float "upload_bits") 1000000.0 }}{{ end }}
|
||||||
|
{{ if eq $upload 0.0 }}{{ $upload = div (.JSON.Float "data.upload_bits") 1000000.0 }}{{ end }}
|
||||||
|
{{ if eq $upload 0.0 }}{{ $upload = div (mul (.JSON.Float "data.data.upload.bandwidth") 8.0) 1000000.0 }}{{ end }}
|
||||||
|
{{ if gt $download 100000.0 }}{{ $download = div (mul $download 8.0) 1000000.0 }}{{ end }}
|
||||||
|
{{ if gt $upload 100000.0 }}{{ $upload = div (mul $upload 8.0) 1000000.0 }}{{ end }}
|
||||||
|
{{ $ping := .JSON.Float "ping" }}
|
||||||
|
{{ if eq $ping 0.0 }}{{ $ping = .JSON.Float "data.ping" }}{{ end }}
|
||||||
|
{{ if eq $ping 0.0 }}{{ $ping = .JSON.Float "data.data.ping.latency" }}{{ end }}
|
||||||
|
<div class="text-center" style="margin-bottom: 10px;">
|
||||||
|
<div class="color-primary size-h3" style="font-weight: 700;">{{ if ne $ip "" }}{{ $ip }}{{ else }}WAN online{{ end }}</div>
|
||||||
|
<div class="size-h6 color-subdue">{{ if ne $isp "" }}{{ $isp }}{{ else }}Speedtest Tracker{{ end }}</div>
|
||||||
|
</div>
|
||||||
|
{{ if and (eq $download 0.0) (eq $upload 0.0) }}
|
||||||
|
<div class="text-center color-subdue size-h6">Keine aktuellen Messdaten</div>
|
||||||
|
{{ else }}
|
||||||
|
<div class="flex justify-between text-center">
|
||||||
|
<div>
|
||||||
|
<div class="color-highlight size-h4">{{ printf "%.1f" $download }}</div>
|
||||||
|
<div class="size-h6 color-subdue">MBIT DOWN</div>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<div class="color-highlight size-h4">{{ printf "%.1f" $upload }}</div>
|
||||||
|
<div class="size-h6 color-subdue">MBIT UP</div>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<div class="color-highlight size-h4">{{ printf "%.0f ms" $ping }}</div>
|
||||||
|
<div class="size-h6 color-subdue">PING</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{{ end }}
|
||||||
|
|
||||||
|
- type: dns-stats
|
||||||
|
title: DNS Stats
|
||||||
|
service: adguard
|
||||||
|
url: http://adguard
|
||||||
|
username: ${GLANCE_ADGUARD_USERNAME}
|
||||||
|
password: ${GLANCE_ADGUARD_PASSWORD}
|
||||||
|
|
||||||
|
- type: custom-api
|
||||||
|
title: Borg Backup
|
||||||
|
title-url: https://borg.kaleschke.info
|
||||||
|
cache: 15m
|
||||||
|
url: http://monitoring-prometheus:9090/api/v1/query?query=(time()-homelab_borg_last_completed_timestamp_seconds)/3600
|
||||||
|
subrequests:
|
||||||
|
success:
|
||||||
|
url: http://monitoring-prometheus:9090/api/v1/query?query=homelab_borg_last_success
|
||||||
|
template: |
|
||||||
|
{{ $ageHours := .JSON.Float "data.result.0.value.1" }}
|
||||||
|
{{ $archive := .JSON.String "data.result.0.metric.archive" }}
|
||||||
|
{{ $succ := .Subrequest "success" }}
|
||||||
|
{{ $ok := $succ.JSON.Float "data.result.0.value.1" }}
|
||||||
|
{{ $status := $succ.JSON.String "data.result.0.metric.status" }}
|
||||||
|
{{ if eq (len (.JSON.Array "data.result")) 0 }}
|
||||||
|
<div class="text-center color-subdue">Keine Backup-Metrik gefunden</div>
|
||||||
|
{{ else }}
|
||||||
|
<div class="text-center">
|
||||||
|
<div class="size-h2 {{ if gt $ageHours 30.0 }}color-negative{{ else }}color-positive{{ end }}">vor {{ printf "%.0f" $ageHours }} h</div>
|
||||||
|
<div class="size-h6 color-subdue" style="margin-top: 4px;">letztes abgeschlossenes Backup</div>
|
||||||
|
<div class="size-h6 {{ if eq $ok 1.0 }}color-positive{{ else }}color-negative{{ end }}" style="margin-top: 6px;">
|
||||||
|
{{ if eq $ok 1.0 }}letzter Job erfolgreich{{ else }}letzter Job: {{ $status }}{{ end }}
|
||||||
|
</div>
|
||||||
|
{{ if ne $archive "" }}<div class="size-h6 color-subdue text-truncate" style="margin-top: 2px;">{{ $archive }}</div>{{ end }}
|
||||||
|
</div>
|
||||||
|
{{ end }}
|
||||||
|
|
||||||
|
- type: group
|
||||||
|
widgets:
|
||||||
|
- type: docker-containers
|
||||||
|
title: Network
|
||||||
|
category: network
|
||||||
|
hide-by-default: true
|
||||||
|
sock-path: tcp://glance-docker-socket-proxy:2375
|
||||||
|
containers:
|
||||||
|
$include: containers-map.yml
|
||||||
|
|
||||||
|
- type: docker-containers
|
||||||
|
title: Apps
|
||||||
|
category: apps
|
||||||
|
hide-by-default: true
|
||||||
|
sock-path: tcp://glance-docker-socket-proxy:2375
|
||||||
|
containers:
|
||||||
|
$include: containers-map.yml
|
||||||
|
|
||||||
|
- type: docker-containers
|
||||||
|
title: Ops
|
||||||
|
category: ops
|
||||||
|
hide-by-default: true
|
||||||
|
sock-path: tcp://glance-docker-socket-proxy:2375
|
||||||
|
containers:
|
||||||
|
$include: containers-map.yml
|
||||||
@@ -0,0 +1,244 @@
|
|||||||
|
- name: Infrastructure and Media
|
||||||
|
slug: infrastructure
|
||||||
|
width: wide
|
||||||
|
columns:
|
||||||
|
- size: small
|
||||||
|
widgets:
|
||||||
|
- type: bookmarks
|
||||||
|
title: Core
|
||||||
|
groups:
|
||||||
|
- title: Control Plane
|
||||||
|
color: 212 100 50
|
||||||
|
links:
|
||||||
|
- title: Komodo
|
||||||
|
url: https://komodo.kaleschke.info
|
||||||
|
icon: https://cdn.jsdelivr.net/gh/homarr-labs/dashboard-icons/svg/komodo.svg
|
||||||
|
- title: Gitea
|
||||||
|
url: https://git.kaleschke.info
|
||||||
|
icon: https://cdn.jsdelivr.net/gh/homarr-labs/dashboard-icons/svg/gitea.svg
|
||||||
|
- title: Traefik
|
||||||
|
url: https://traefik.kaleschke.info
|
||||||
|
icon: https://cdn.jsdelivr.net/gh/homarr-labs/dashboard-icons/svg/traefik.svg
|
||||||
|
- title: Authelia
|
||||||
|
url: https://auth.kaleschke.info
|
||||||
|
icon: https://cdn.jsdelivr.net/gh/homarr-labs/dashboard-icons/svg/authelia.svg
|
||||||
|
|
||||||
|
- type: bookmarks
|
||||||
|
title: Media und Apps
|
||||||
|
groups:
|
||||||
|
- title: Apps
|
||||||
|
color: 140 70 40
|
||||||
|
links:
|
||||||
|
- title: Immich
|
||||||
|
url: https://immich.kaleschke.info
|
||||||
|
icon: https://cdn.jsdelivr.net/gh/homarr-labs/dashboard-icons/svg/immich.svg
|
||||||
|
- title: Paperless
|
||||||
|
url: https://paperless.kaleschke.info
|
||||||
|
icon: https://cdn.jsdelivr.net/gh/homarr-labs/dashboard-icons/svg/paperless-ngx.svg
|
||||||
|
- title: Nextcloud
|
||||||
|
url: https://cloud.kaleschke.info
|
||||||
|
icon: https://cdn.jsdelivr.net/gh/homarr-labs/dashboard-icons/svg/nextcloud.svg
|
||||||
|
- title: Mealie
|
||||||
|
url: https://mealie.kaleschke.info
|
||||||
|
icon: https://cdn.jsdelivr.net/gh/homarr-labs/dashboard-icons/svg/mealie.svg
|
||||||
|
|
||||||
|
- type: custom-api
|
||||||
|
title: Scrutiny Disk Health
|
||||||
|
title-url: https://scrutiny.kaleschke.info
|
||||||
|
cache: 30m
|
||||||
|
url: http://scrutiny:8080/api/summary
|
||||||
|
template: |
|
||||||
|
{{ $disks := .JSON.Array "data.summary.@values" }}
|
||||||
|
{{ if eq (len $disks) 0 }}
|
||||||
|
<div class="text-center color-subdue">Keine Disks gemeldet.</div>
|
||||||
|
{{ else }}
|
||||||
|
<ul class="list list-gap-4">
|
||||||
|
{{ range $disks }}
|
||||||
|
{{ $status := .Int "device.device_status" }}
|
||||||
|
<li class="flex justify-between">
|
||||||
|
<div class="color-highlight">{{ .String "device.device_name" }}</div>
|
||||||
|
<div class="size-h6 uppercase {{ if eq $status 0 }}color-positive{{ else }}color-negative{{ end }}">
|
||||||
|
{{ if eq $status 0 }}OK{{ else }}FAILED{{ end }}
|
||||||
|
</div>
|
||||||
|
</li>
|
||||||
|
{{ end }}
|
||||||
|
</ul>
|
||||||
|
{{ end }}
|
||||||
|
|
||||||
|
- size: full
|
||||||
|
widgets:
|
||||||
|
- type: custom-api
|
||||||
|
title: GitOps - homelab-infra
|
||||||
|
title-url: https://git.kaleschke.info/Micha/homelab-infra
|
||||||
|
cache: 5m
|
||||||
|
url: http://gitea:3000/api/v1/repos/Micha/homelab-infra/commits?limit=5&stat=false
|
||||||
|
headers:
|
||||||
|
Authorization: token ${GLANCE_GITEA_TOKEN}
|
||||||
|
Accept: application/json
|
||||||
|
subrequests:
|
||||||
|
repo:
|
||||||
|
url: http://gitea:3000/api/v1/repos/Micha/homelab-infra
|
||||||
|
headers:
|
||||||
|
Authorization: token ${GLANCE_GITEA_TOKEN}
|
||||||
|
Accept: application/json
|
||||||
|
template: |
|
||||||
|
{{ $repo := .Subrequest "repo" }}
|
||||||
|
{{ $repoOK := and (ge $repo.Response.StatusCode 200) (le $repo.Response.StatusCode 299) }}
|
||||||
|
{{ if $repoOK }}
|
||||||
|
<div class="flex justify-between text-center" style="margin-bottom: 12px;">
|
||||||
|
<div>
|
||||||
|
<div class="color-highlight size-h3">{{ $repo.JSON.Int "open_issues_count" }}</div>
|
||||||
|
<div class="size-h6 uppercase">Issues</div>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<div class="color-highlight size-h3">{{ $repo.JSON.Int "open_pr_counter" }}</div>
|
||||||
|
<div class="size-h6 uppercase">PRs</div>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<div class="color-highlight size-h3">{{ $repo.JSON.String "default_branch" }}</div>
|
||||||
|
<div class="size-h6 uppercase">Branch</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{{ end }}
|
||||||
|
<ul class="list list-gap-6">
|
||||||
|
{{ range .JSON.Array "@this" }}
|
||||||
|
<li>
|
||||||
|
<div class="flex justify-between">
|
||||||
|
<div class="color-highlight text-truncate" style="max-width: 75%;">{{ .String "commit.message" | replaceMatches "(?s)\n.*" "" }}</div>
|
||||||
|
<div class="size-h6 color-subdue">{{ slice (.String "sha") 0 7 }}</div>
|
||||||
|
</div>
|
||||||
|
<div class="size-h6 color-subdue">{{ .String "commit.author.name" }} · <span {{ .String "commit.author.date" | parseTime "rfc3339" | toRelativeTime }}></span></div>
|
||||||
|
</li>
|
||||||
|
{{ end }}
|
||||||
|
</ul>
|
||||||
|
|
||||||
|
- type: monitor
|
||||||
|
title: Platform Checks
|
||||||
|
cache: 1m
|
||||||
|
sites:
|
||||||
|
- title: Gitea
|
||||||
|
url: https://git.kaleschke.info
|
||||||
|
check-url: http://gitea:3000/api/healthz
|
||||||
|
icon: https://cdn.jsdelivr.net/gh/homarr-labs/dashboard-icons/svg/gitea.svg
|
||||||
|
timeout: 5s
|
||||||
|
alt-status-codes: [200, 302, 401, 403]
|
||||||
|
- title: Monitoring Grafana
|
||||||
|
url: https://monitoring.kaleschke.info
|
||||||
|
check-url: http://monitoring-grafana:3000/api/health
|
||||||
|
icon: https://cdn.jsdelivr.net/gh/homarr-labs/dashboard-icons/svg/grafana.svg
|
||||||
|
timeout: 5s
|
||||||
|
alt-status-codes: [200, 302, 401, 403]
|
||||||
|
- title: Glance
|
||||||
|
url: https://glance.kaleschke.info
|
||||||
|
check-url: http://glance:8080
|
||||||
|
icon: https://cdn.jsdelivr.net/gh/homarr-labs/dashboard-icons/svg/glance.svg
|
||||||
|
timeout: 5s
|
||||||
|
alt-status-codes: [200, 302, 401, 403]
|
||||||
|
- title: Immich
|
||||||
|
url: https://immich.kaleschke.info
|
||||||
|
check-url: http://immich_server:2283/api/server/ping
|
||||||
|
icon: https://cdn.jsdelivr.net/gh/homarr-labs/dashboard-icons/svg/immich.svg
|
||||||
|
timeout: 5s
|
||||||
|
alt-status-codes: [200, 302, 401, 403]
|
||||||
|
- title: Paperless-ngx
|
||||||
|
url: https://paperless.kaleschke.info
|
||||||
|
check-url: http://paperless-ngx:8000
|
||||||
|
icon: https://cdn.jsdelivr.net/gh/homarr-labs/dashboard-icons/svg/paperless-ngx.svg
|
||||||
|
timeout: 5s
|
||||||
|
alt-status-codes: [200, 302, 401, 403]
|
||||||
|
- title: Nextcloud
|
||||||
|
url: https://cloud.kaleschke.info
|
||||||
|
check-url: http://nextcloud/status.php
|
||||||
|
icon: https://cdn.jsdelivr.net/gh/homarr-labs/dashboard-icons/svg/nextcloud.svg
|
||||||
|
timeout: 5s
|
||||||
|
alt-status-codes: [200, 302, 401, 403]
|
||||||
|
|
||||||
|
- type: group
|
||||||
|
widgets:
|
||||||
|
- type: docker-containers
|
||||||
|
title: Core
|
||||||
|
category: core
|
||||||
|
hide-by-default: true
|
||||||
|
sock-path: tcp://glance-docker-socket-proxy:2375
|
||||||
|
containers:
|
||||||
|
$include: containers-map.yml
|
||||||
|
|
||||||
|
- type: docker-containers
|
||||||
|
title: Apps
|
||||||
|
category: apps
|
||||||
|
hide-by-default: true
|
||||||
|
sock-path: tcp://glance-docker-socket-proxy:2375
|
||||||
|
containers:
|
||||||
|
$include: containers-map.yml
|
||||||
|
|
||||||
|
- type: docker-containers
|
||||||
|
title: Ops
|
||||||
|
category: ops
|
||||||
|
hide-by-default: true
|
||||||
|
sock-path: tcp://glance-docker-socket-proxy:2375
|
||||||
|
containers:
|
||||||
|
$include: containers-map.yml
|
||||||
|
|
||||||
|
- size: small
|
||||||
|
widgets:
|
||||||
|
- type: custom-api
|
||||||
|
title: Paperless-ngx
|
||||||
|
title-url: https://paperless.kaleschke.info
|
||||||
|
cache: 15m
|
||||||
|
url: http://paperless-ngx:8000/api/statistics/
|
||||||
|
headers:
|
||||||
|
Authorization: Token ${GLANCE_PAPERLESS_TOKEN}
|
||||||
|
Accept: application/json
|
||||||
|
template: |
|
||||||
|
{{ $total := .JSON.Int "documents_total" }}
|
||||||
|
{{ $inbox := .JSON.Int "documents_inbox" }}
|
||||||
|
<div class="flex justify-between text-center">
|
||||||
|
<div>
|
||||||
|
<div class="color-highlight size-h3">{{ $total | formatNumber }}</div>
|
||||||
|
<div class="size-h6 uppercase">Dokumente</div>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<div class="size-h3 {{ if gt $inbox 0 }}color-negative{{ else }}color-positive{{ end }}">{{ $inbox }}</div>
|
||||||
|
<div class="size-h6 uppercase">Inbox</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
- type: custom-api
|
||||||
|
title: Mealie
|
||||||
|
title-url: https://mealie.kaleschke.info
|
||||||
|
cache: 1h
|
||||||
|
url: http://mealie:9000/api/admin/about/statistics
|
||||||
|
headers:
|
||||||
|
Authorization: Bearer ${GLANCE_MEALIE_TOKEN}
|
||||||
|
Accept: application/json
|
||||||
|
template: |
|
||||||
|
<div class="flex justify-between text-center">
|
||||||
|
<div>
|
||||||
|
<div class="color-highlight size-h3">{{ .JSON.Int "totalRecipes" | formatNumber }}</div>
|
||||||
|
<div class="size-h6 uppercase">Rezepte</div>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<div class="color-highlight size-h3">{{ .JSON.Int "totalCategories" }}</div>
|
||||||
|
<div class="size-h6 uppercase">Kategorien</div>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<div class="color-highlight size-h3">{{ .JSON.Int "totalUsers" }}</div>
|
||||||
|
<div class="size-h6 uppercase">Nutzer</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
- type: bookmarks
|
||||||
|
title: Ops
|
||||||
|
groups:
|
||||||
|
- title: Tools
|
||||||
|
color: 4 78 57
|
||||||
|
links:
|
||||||
|
- title: Glances
|
||||||
|
url: https://glances.kaleschke.info
|
||||||
|
icon: https://cdn.jsdelivr.net/gh/homarr-labs/dashboard-icons/svg/glances.svg
|
||||||
|
- title: Scrutiny
|
||||||
|
url: https://scrutiny.kaleschke.info
|
||||||
|
icon: https://cdn.jsdelivr.net/gh/homarr-labs/dashboard-icons/svg/scrutiny.svg
|
||||||
|
- title: Speedtest
|
||||||
|
url: https://speedtest.kaleschke.info
|
||||||
|
icon: https://cdn.jsdelivr.net/gh/homarr-labs/dashboard-icons/png/speedtest-tracker.png
|
||||||
@@ -0,0 +1,80 @@
|
|||||||
|
- name: Ops und Releases
|
||||||
|
slug: ops
|
||||||
|
width: wide
|
||||||
|
columns:
|
||||||
|
- size: small
|
||||||
|
widgets:
|
||||||
|
- type: rss
|
||||||
|
title: Selfhosted News
|
||||||
|
style: vertical-list
|
||||||
|
limit: 12
|
||||||
|
collapse-after: 6
|
||||||
|
cache: 6h
|
||||||
|
feeds:
|
||||||
|
- url: https://selfh.st/rss/
|
||||||
|
title: selfh.st
|
||||||
|
- url: https://tailscale.com/blog/index.xml
|
||||||
|
title: Tailscale Blog
|
||||||
|
|
||||||
|
- size: full
|
||||||
|
widgets:
|
||||||
|
- type: releases
|
||||||
|
title: Image Releases
|
||||||
|
cache: 12h
|
||||||
|
show-source-icon: true
|
||||||
|
collapse-after: 15
|
||||||
|
repositories:
|
||||||
|
- glanceapp/glance
|
||||||
|
- traefik/traefik
|
||||||
|
- go-gitea/gitea
|
||||||
|
- moghtech/komodo
|
||||||
|
- immich-app/immich
|
||||||
|
- paperless-ngx/paperless-ngx
|
||||||
|
- AdguardTeam/AdGuardHome
|
||||||
|
- dani-garcia/vaultwarden
|
||||||
|
- authelia/authelia
|
||||||
|
- mealie-recipes/mealie
|
||||||
|
- nextcloud/server
|
||||||
|
- AnalogJ/scrutiny
|
||||||
|
- alexjustesen/speedtest-tracker
|
||||||
|
- binwiederhier/ntfy
|
||||||
|
- filebrowser/filebrowser
|
||||||
|
- coder/code-server
|
||||||
|
- qdm12/ddns-updater
|
||||||
|
- nicolargo/glances
|
||||||
|
|
||||||
|
- size: small
|
||||||
|
widgets:
|
||||||
|
- type: custom-api
|
||||||
|
title: Letzte Commits
|
||||||
|
title-url: https://git.kaleschke.info/Micha/homelab-infra/commits/branch/master
|
||||||
|
cache: 5m
|
||||||
|
url: http://gitea:3000/api/v1/repos/Micha/homelab-infra/commits?limit=8&stat=false
|
||||||
|
headers:
|
||||||
|
Authorization: token ${GLANCE_GITEA_TOKEN}
|
||||||
|
Accept: application/json
|
||||||
|
template: |
|
||||||
|
<ul class="list list-gap-6 collapsible-container" data-collapse-after="5">
|
||||||
|
{{ range .JSON.Array "@this" }}
|
||||||
|
<li>
|
||||||
|
<div class="color-highlight text-truncate">{{ .String "commit.message" | replaceMatches "(?s)\n.*" "" }}</div>
|
||||||
|
<div class="size-h6 color-subdue">{{ slice (.String "sha") 0 7 }} · <span {{ .String "commit.author.date" | parseTime "rfc3339" | toRelativeTime }}></span></div>
|
||||||
|
</li>
|
||||||
|
{{ end }}
|
||||||
|
</ul>
|
||||||
|
|
||||||
|
- type: bookmarks
|
||||||
|
title: Deploy-Kette
|
||||||
|
groups:
|
||||||
|
- title: GitOps
|
||||||
|
color: 212 100 50
|
||||||
|
links:
|
||||||
|
- title: Gitea Repo
|
||||||
|
url: https://git.kaleschke.info/Micha/homelab-infra
|
||||||
|
icon: si:gitea
|
||||||
|
- title: Komodo Stacks
|
||||||
|
url: https://komodo.kaleschke.info
|
||||||
|
icon: sh:komodo
|
||||||
|
- title: Grafana
|
||||||
|
url: https://monitoring.kaleschke.info
|
||||||
|
icon: si:grafana
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
$include: home.yml
|
||||||
|
$include: infrastructure.yml
|
||||||
|
$include: ops.yml
|
||||||
@@ -9,11 +9,19 @@ services:
|
|||||||
GLANCE_ADGUARD_USERNAME: ${GLANCE_ADGUARD_USERNAME:-}
|
GLANCE_ADGUARD_USERNAME: ${GLANCE_ADGUARD_USERNAME:-}
|
||||||
GLANCE_ADGUARD_PASSWORD: ${GLANCE_ADGUARD_PASSWORD:-}
|
GLANCE_ADGUARD_PASSWORD: ${GLANCE_ADGUARD_PASSWORD:-}
|
||||||
GLANCE_SPEEDTEST_API_KEY: ${GLANCE_SPEEDTEST_API_KEY:-}
|
GLANCE_SPEEDTEST_API_KEY: ${GLANCE_SPEEDTEST_API_KEY:-}
|
||||||
|
GLANCE_KOMODO_API_KEY: ${GLANCE_KOMODO_API_KEY:-}
|
||||||
|
GLANCE_KOMODO_API_SECRET: ${GLANCE_KOMODO_API_SECRET:-}
|
||||||
|
GLANCE_GITEA_TOKEN: ${GLANCE_GITEA_TOKEN:-}
|
||||||
|
GLANCE_PAPERLESS_TOKEN: ${GLANCE_PAPERLESS_TOKEN:-}
|
||||||
|
GLANCE_MEALIE_TOKEN: ${GLANCE_MEALIE_TOKEN:-}
|
||||||
volumes:
|
volumes:
|
||||||
- ./config:/app/config:ro
|
- ./config:/app/config:ro
|
||||||
|
- ./assets:/app/assets:ro
|
||||||
networks:
|
networks:
|
||||||
- frontend_net
|
- frontend_net
|
||||||
- glance_socket_net
|
- glance_socket_net
|
||||||
|
# monitoring_net nur lesend fuer Prometheus-Query des Borg-Backup-Widgets
|
||||||
|
- monitoring_net
|
||||||
depends_on:
|
depends_on:
|
||||||
- glance-docker-socket-proxy
|
- glance-docker-socket-proxy
|
||||||
labels:
|
labels:
|
||||||
@@ -50,6 +58,8 @@ services:
|
|||||||
networks:
|
networks:
|
||||||
frontend_net:
|
frontend_net:
|
||||||
external: true
|
external: true
|
||||||
|
monitoring_net:
|
||||||
|
external: true
|
||||||
glance_socket_net:
|
glance_socket_net:
|
||||||
name: glance_socket_net
|
name: glance_socket_net
|
||||||
internal: true
|
internal: true
|
||||||
|
|||||||
@@ -0,0 +1,237 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
# Komodo-Stack-Hygiene-Check.
|
||||||
|
#
|
||||||
|
# Prueft, dass jeder Komodo-Stack sauber gegen das Git-Repo konfiguriert ist,
|
||||||
|
# und dass jeder Compose-File im Repo einen passenden Komodo-Stack hat.
|
||||||
|
# Findet die Klasse von Fehlern, die `immich_new` (2026-06-12) durchgelassen
|
||||||
|
# hat: Stack RUNNING, aber kein Repo / kein Account / project_missing.
|
||||||
|
|
||||||
|
REPO_ROOT="${REPO_ROOT:-$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd)}"
|
||||||
|
OUTPUT_PATH="${OUTPUT_PATH:-/mnt/user/services/posture-check/komodo-stack-hygiene-last.json}"
|
||||||
|
NTFY_SCRIPT="${NTFY_SCRIPT:-$REPO_ROOT/ops/restore-tests/send-ntfy.sh}"
|
||||||
|
NTFY_TOPIC="${NTFY_TOPIC:-homelab-alerts}"
|
||||||
|
SEND_NTFY="${SEND_NTFY:-1}"
|
||||||
|
KOMODO_ENV_FILE="${KOMODO_ENV_FILE:-/mnt/user/appdata/secrets/codex_komodo_api.env}"
|
||||||
|
KOMODO_CONTAINER="${KOMODO_CONTAINER:-komodo-core}"
|
||||||
|
|
||||||
|
# Komma-separierte Allowlist fuer bewusst inline-managed Stacks.
|
||||||
|
# Quelle: memory/komodo-stack-inline-managed.md, CLAUDE.md.
|
||||||
|
INLINE_ALLOWLIST="${INLINE_ALLOWLIST:-komodo,grafana}"
|
||||||
|
|
||||||
|
# Compose-Files unter diesen Pfaden zaehlen NICHT als erwartete Stacks
|
||||||
|
# (Beispiele, Archive, Submodule).
|
||||||
|
COMPOSE_EXCLUDE_PATTERN="${COMPOSE_EXCLUDE_PATTERN:-/archive/|/examples/|/.git/}"
|
||||||
|
|
||||||
|
# Compose-Dir-Namen, die bewusst NICHT als Komodo-Stack laufen sollen
|
||||||
|
# (Work-in-progress, Build-/Dev-Compose, manuell deployed). Komma-separiert.
|
||||||
|
EXPECTED_NOT_IN_KOMODO="${EXPECTED_NOT_IN_KOMODO:-hermes-agent}"
|
||||||
|
|
||||||
|
TMP_DIR="${TMP_DIR:-/tmp/kallilab-komodo-stack-hygiene}"
|
||||||
|
mkdir -p "$TMP_DIR"
|
||||||
|
RESULTS_FILE="$TMP_DIR/results.$$"
|
||||||
|
STACKS_FILE="$TMP_DIR/stacks.$$.json"
|
||||||
|
: > "$RESULTS_FILE"
|
||||||
|
trap 'rm -f "$RESULTS_FILE" "$STACKS_FILE"' EXIT
|
||||||
|
|
||||||
|
json_escape() {
|
||||||
|
sed -e 's/\\/\\\\/g' -e 's/"/\\"/g' -e 's/\t/\\t/g'
|
||||||
|
}
|
||||||
|
|
||||||
|
add_result() {
|
||||||
|
printf '%s\t%s\t%s\n' "$1" "$2" "$3" >> "$RESULTS_FILE"
|
||||||
|
}
|
||||||
|
|
||||||
|
is_inline_allowed() {
|
||||||
|
local name="$1"
|
||||||
|
local IFS=,
|
||||||
|
for entry in $INLINE_ALLOWLIST; do
|
||||||
|
[ "$name" = "$entry" ] && return 0
|
||||||
|
done
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
|
||||||
|
is_expected_not_in_komodo() {
|
||||||
|
local name="$1"
|
||||||
|
local IFS=,
|
||||||
|
for entry in $EXPECTED_NOT_IN_KOMODO; do
|
||||||
|
[ "$name" = "$entry" ] && return 0
|
||||||
|
done
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
|
||||||
|
# True drift: do files inside this stack's compose-dir actually differ
|
||||||
|
# between deployed_hash and latest_hash? Komodo's deployed_hash bumps only
|
||||||
|
# on redeploy, while latest_hash tracks master HEAD - that produces a noisy
|
||||||
|
# "Pending Update" even when the stack itself wasn't touched.
|
||||||
|
stack_files_changed() {
|
||||||
|
local name="$1" deployed="$2" latest="$3"
|
||||||
|
local dir
|
||||||
|
# Locate the stack's compose dir (case-insensitive, same as Mode 3).
|
||||||
|
dir="$(find "$REPO_ROOT" -type d -iname "$name" -not -path "*/.git/*" 2>/dev/null | head -1)"
|
||||||
|
[ -n "$dir" ] || return 0 # No dir -> can't tell, treat as drift to be safe
|
||||||
|
( cd "$REPO_ROOT" && git rev-parse --verify --quiet "$deployed" >/dev/null ) || return 0
|
||||||
|
( cd "$REPO_ROOT" && git rev-parse --verify --quiet "$latest" >/dev/null ) || return 0
|
||||||
|
local rel="${dir#$REPO_ROOT/}"
|
||||||
|
if ( cd "$REPO_ROOT" && git diff --quiet "$deployed".."$latest" -- "$rel" ); then
|
||||||
|
return 1 # no change
|
||||||
|
fi
|
||||||
|
return 0 # real change
|
||||||
|
}
|
||||||
|
|
||||||
|
# Komodo-API-Credentials laden und Stack-Liste holen.
|
||||||
|
if [ ! -r "$KOMODO_ENV_FILE" ]; then
|
||||||
|
add_result "warning" "komodo-api" "Komodo env file not readable: $KOMODO_ENV_FILE"
|
||||||
|
else
|
||||||
|
set -a
|
||||||
|
# shellcheck disable=SC1090
|
||||||
|
. "$KOMODO_ENV_FILE"
|
||||||
|
set +a
|
||||||
|
if ! docker exec \
|
||||||
|
-e KOMODO_CLI_HOST \
|
||||||
|
-e KOMODO_CLI_KEY \
|
||||||
|
-e KOMODO_CLI_SECRET \
|
||||||
|
"$KOMODO_CONTAINER" km list -a stacks -f json > "$STACKS_FILE" 2>/dev/null; then
|
||||||
|
add_result "warning" "komodo-api" "km list stacks failed (container=$KOMODO_CONTAINER)"
|
||||||
|
: > "$STACKS_FILE"
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Per-Stack-Checks. Trenner: "|" statt Tab, weil IFS=Tab leere Felder kollabiert
|
||||||
|
# (Tab ist Whitespace in IFS). "|" kommt in Stack-Namen/Repos/Hashes nicht vor.
|
||||||
|
if [ -s "$STACKS_FILE" ]; then
|
||||||
|
while IFS='|' read -r name repo project_missing missing_files state deployed_hash latest_hash files_on_host file_contents; do
|
||||||
|
[ -n "$name" ] || continue
|
||||||
|
|
||||||
|
if is_inline_allowed "$name"; then
|
||||||
|
add_result "ok" "$name" "Inline-managed (allowlisted), skipping repo checks"
|
||||||
|
continue
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Failure-Mode 1: Stack hat keine Git-Quelle (immich_new-Symptom).
|
||||||
|
if [ "$repo" = "-" ] && [ "$files_on_host" != "True" ] && [ "$file_contents" != "True" ]; then
|
||||||
|
add_result "critical" "$name" "Stack has no repo configured and is not inline-allowed"
|
||||||
|
continue
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Failure-Mode 2: Komodo meldet Project Missing.
|
||||||
|
if [ "$project_missing" = "True" ]; then
|
||||||
|
add_result "critical" "$name" "project_missing=true (missing_files=$missing_files)"
|
||||||
|
continue
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Failure-Mode 3: Stack-Name passt zu keinem Compose-File im Repo.
|
||||||
|
# Case-insensitive (Compose-Dir kann GroSs/klein abweichen, z.B. Adguard).
|
||||||
|
match_found=""
|
||||||
|
while IFS= read -r dir; do
|
||||||
|
[ -n "$dir" ] || continue
|
||||||
|
if [ -f "$dir/docker-compose.yml" ] \
|
||||||
|
|| [ -f "$dir/docker-compose.yaml" ] \
|
||||||
|
|| [ -f "$dir/compose.yml" ] \
|
||||||
|
|| [ -f "$dir/compose.yaml" ]; then
|
||||||
|
match_found=1
|
||||||
|
break
|
||||||
|
fi
|
||||||
|
done < <(find "$REPO_ROOT" -type d -iname "$name" -not -path "*/.git/*" 2>/dev/null)
|
||||||
|
if [ -z "$match_found" ]; then
|
||||||
|
# Verwaiste Stacks wie das frueher gesehene `immich_new`: Komodo kennt
|
||||||
|
# ihn, aber im Repo gibt's keinen Compose-Pfad.
|
||||||
|
add_result "warning" "$name" "Stack name does not match any compose directory in repo"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Failure-Mode 4: Deployed-Hash hinkt latest hinterher UND der Stack-Dir
|
||||||
|
# hat tatsaechlich File-Aenderungen dazwischen. Reine Komodo-Hash-Bewegung
|
||||||
|
# ohne Stack-Inhalt aendert nichts und ist kein echter Drift.
|
||||||
|
# "-" = unbekannt (z.B. gitea self-host edge case), nicht als Drift werten.
|
||||||
|
if [ "$deployed_hash" != "-" ] && [ "$latest_hash" != "-" ] \
|
||||||
|
&& [ "$deployed_hash" != "$latest_hash" ] \
|
||||||
|
&& stack_files_changed "$name" "$deployed_hash" "$latest_hash"; then
|
||||||
|
add_result "warning" "$name" "deployed_hash $deployed_hash != latest_hash $latest_hash (stack files changed)"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Failure-Mode 5: Stack ist down.
|
||||||
|
if [ "$state" = "down" ] || [ "$state" = "unknown" ]; then
|
||||||
|
add_result "warning" "$name" "Stack state is $state"
|
||||||
|
fi
|
||||||
|
|
||||||
|
add_result "ok" "$name" "Stack hygiene OK (state=$state, hash=$deployed_hash)"
|
||||||
|
done < <(jq -r '.[] | [
|
||||||
|
.name // "-",
|
||||||
|
(.info.repo // "-"),
|
||||||
|
(.info.project_missing | if . then "True" else "False" end),
|
||||||
|
(((.info.missing_files // []) | join(",")) | if . == "" then "-" else . end),
|
||||||
|
(.info.state // "-"),
|
||||||
|
(.info.deployed_hash // "-"),
|
||||||
|
(.info.latest_hash // "-"),
|
||||||
|
(.info.files_on_host | if . then "True" else "False" end),
|
||||||
|
(.info.file_contents | if . then "True" else "False" end)
|
||||||
|
] | join("|")' "$STACKS_FILE")
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Failure-Mode 6: Compose-File im Repo, aber kein Komodo-Stack mit gleichem Namen.
|
||||||
|
if [ -s "$STACKS_FILE" ]; then
|
||||||
|
known_names="$(jq -r '.[].name' "$STACKS_FILE")"
|
||||||
|
while IFS= read -r -d '' compose; do
|
||||||
|
rel="${compose#$REPO_ROOT/}"
|
||||||
|
if printf '%s' "$rel" | grep -Eq "$COMPOSE_EXCLUDE_PATTERN"; then
|
||||||
|
continue
|
||||||
|
fi
|
||||||
|
dir_name="$(basename "$(dirname "$compose")")"
|
||||||
|
if is_inline_allowed "$dir_name"; then
|
||||||
|
continue
|
||||||
|
fi
|
||||||
|
if is_expected_not_in_komodo "$dir_name"; then
|
||||||
|
continue
|
||||||
|
fi
|
||||||
|
# Case-insensitive, weil z.B. host-services/Adguard <-> Komodo-Stack adguard
|
||||||
|
# legitim als gematched gilt.
|
||||||
|
if ! printf '%s\n' "$known_names" | grep -Fixq "$dir_name"; then
|
||||||
|
add_result "warning" "$dir_name" "Compose file $rel has no matching Komodo stack"
|
||||||
|
fi
|
||||||
|
done < <(find "$REPO_ROOT" -path "$REPO_ROOT/.git" -prune -o -type f \
|
||||||
|
\( -name docker-compose.yml -o -name docker-compose.yaml \
|
||||||
|
-o -name compose.yml -o -name compose.yaml \) -print0)
|
||||||
|
fi
|
||||||
|
|
||||||
|
timestamp="$(date -Iseconds)"
|
||||||
|
critical_count="$(awk -F '\t' '$1 == "critical" { c++ } END { print c + 0 }' "$RESULTS_FILE")"
|
||||||
|
warning_count="$(awk -F '\t' '$1 == "warning" { c++ } END { print c + 0 }' "$RESULTS_FILE")"
|
||||||
|
status="ok"
|
||||||
|
[ "$warning_count" -gt 0 ] && status="warning"
|
||||||
|
[ "$critical_count" -gt 0 ] && status="critical"
|
||||||
|
|
||||||
|
mkdir -p "$(dirname "$OUTPUT_PATH")"
|
||||||
|
{
|
||||||
|
printf '{\n'
|
||||||
|
printf ' "timestamp": "%s",\n' "$(printf '%s' "$timestamp" | json_escape)"
|
||||||
|
printf ' "status": "%s",\n' "$status"
|
||||||
|
printf ' "critical_count": %s,\n' "$critical_count"
|
||||||
|
printf ' "warning_count": %s,\n' "$warning_count"
|
||||||
|
printf ' "checks": [\n'
|
||||||
|
first=1
|
||||||
|
while IFS=$'\t' read -r severity name message; do
|
||||||
|
if [ "$first" -eq 0 ]; then printf ',\n'; fi
|
||||||
|
first=0
|
||||||
|
printf ' {"severity":"%s","name":"%s","message":"%s"}' \
|
||||||
|
"$(printf '%s' "$severity" | json_escape)" \
|
||||||
|
"$(printf '%s' "$name" | json_escape)" \
|
||||||
|
"$(printf '%s' "$message" | json_escape)"
|
||||||
|
done < "$RESULTS_FILE"
|
||||||
|
printf '\n ]\n}\n'
|
||||||
|
} > "$OUTPUT_PATH.tmp"
|
||||||
|
mv "$OUTPUT_PATH.tmp" "$OUTPUT_PATH"
|
||||||
|
cat "$OUTPUT_PATH"
|
||||||
|
|
||||||
|
if [ "$critical_count" -gt 0 ] || [ "$warning_count" -gt 0 ]; then
|
||||||
|
if [ "$SEND_NTFY" = "1" ] && [ -x "$NTFY_SCRIPT" ]; then
|
||||||
|
priority="default"
|
||||||
|
[ "$warning_count" -gt 0 ] && priority="high"
|
||||||
|
[ "$critical_count" -gt 0 ] && priority="urgent"
|
||||||
|
"$NTFY_SCRIPT" "$NTFY_TOPIC" \
|
||||||
|
"Komodo stack hygiene: $critical_count critical, $warning_count warning" \
|
||||||
|
"See $OUTPUT_PATH" "$priority" || true
|
||||||
|
fi
|
||||||
|
[ "$critical_count" -gt 0 ] && exit 2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
@@ -0,0 +1,58 @@
|
|||||||
|
# Smart Home Runtime Stack
|
||||||
|
|
||||||
|
Runtime-Zustand fuer Home Assistant auf Kallilabcore. Dieser Ordner gehoert zu
|
||||||
|
`homelab-infra`, weil Komodo den Stack deployt und Renovate die Images pflegt.
|
||||||
|
|
||||||
|
## Dienste
|
||||||
|
|
||||||
|
- `homeassistant`: Home Assistant Container, erreichbar ueber Traefik unter
|
||||||
|
`https://home.kaleschke.info`
|
||||||
|
- `smarthome-mosquitto`: interner MQTT-Broker fuer Home Assistant, spaeter
|
||||||
|
Zigbee2MQTT und ESPHome
|
||||||
|
|
||||||
|
## Abhaengigkeiten
|
||||||
|
|
||||||
|
- `frontend_net` existiert bereits und wird von Traefik genutzt.
|
||||||
|
- `smarthome_net` wird durch diesen Stack angelegt und ist `internal: true`.
|
||||||
|
- Das Fachrepo `smart-home-kalli` muss auf dem Unraid-Host unter
|
||||||
|
`/mnt/user/services/smart-home-kalli` liegen. Nur ausgewählte YAML-Dateien
|
||||||
|
werden read-only nach `/config` gemountet; `.storage` bleibt in
|
||||||
|
`/mnt/user/appdata/homeassistant`.
|
||||||
|
- Vor dem ersten Start muessen diese Dateien hostseitig angelegt werden:
|
||||||
|
- `/mnt/user/appdata/homeassistant/secrets.yaml`
|
||||||
|
- `/mnt/user/appdata/homeassistant/trusted_proxies.yaml`
|
||||||
|
- `/mnt/user/appdata/mosquitto/config/passwordfile`
|
||||||
|
- `/mnt/user/appdata/mosquitto/config/aclfile`
|
||||||
|
|
||||||
|
Das detaillierte Host-Bootstrap-Runbook liegt unter
|
||||||
|
`docs/runbooks/smart-home-bootstrap.md`.
|
||||||
|
|
||||||
|
## MQTT Bootstrap
|
||||||
|
|
||||||
|
Beispiel fuer den initialen Home-Assistant-MQTT-User auf dem Unraid-Host:
|
||||||
|
|
||||||
|
```sh
|
||||||
|
mkdir -p /mnt/user/appdata/mosquitto/config
|
||||||
|
docker run --rm -it \
|
||||||
|
-v /mnt/user/appdata/mosquitto/config:/mosquitto/external_config \
|
||||||
|
eclipse-mosquitto:2.0.22 \
|
||||||
|
mosquitto_passwd -c /mosquitto/external_config/passwordfile homeassistant
|
||||||
|
cat > /mnt/user/appdata/mosquitto/config/aclfile <<'EOF'
|
||||||
|
user homeassistant
|
||||||
|
topic readwrite #
|
||||||
|
EOF
|
||||||
|
```
|
||||||
|
|
||||||
|
LAN-Port `1883` bleibt in Phase 1 geschlossen. Eine Portfreigabe fuer externe
|
||||||
|
MQTT-Clients wird erst in der ESPHome-Phase mit ACLs und per-Device-Usern
|
||||||
|
ergaenzt.
|
||||||
|
|
||||||
|
## Ecowitt
|
||||||
|
|
||||||
|
Ecowitt wird nicht in Phase 1 exponiert. Wegen des globalen Traefik
|
||||||
|
HTTP-zu-HTTPS-Redirects bleibt die Ingress-Entscheidung offen:
|
||||||
|
|
||||||
|
1. Traefik-HTTP-Ausnahme nur fuer den Ecowitt-Webhook, falls der globale
|
||||||
|
EntryPoint-Redirect gezielt abloesbar ist.
|
||||||
|
2. Dokumentierter LAN-only Host-Port `8123` als Fallback, wenn Option 1 den
|
||||||
|
bestehenden Traefik-Standard zu stark verbiegt.
|
||||||
@@ -0,0 +1,58 @@
|
|||||||
|
services:
|
||||||
|
homeassistant:
|
||||||
|
image: ghcr.io/home-assistant/home-assistant:2026.6.1@sha256:59aa8824955c9db491b75d2eebe42bd68494f80c2ec69ec0d66d9dae37d37514
|
||||||
|
container_name: homeassistant
|
||||||
|
restart: unless-stopped
|
||||||
|
environment:
|
||||||
|
TZ: Europe/Berlin
|
||||||
|
volumes:
|
||||||
|
- /mnt/user/appdata/homeassistant:/config
|
||||||
|
- /mnt/user/services/smart-home-kalli/home-assistant/configuration.yaml:/config/configuration.yaml:ro
|
||||||
|
- /mnt/user/services/smart-home-kalli/home-assistant/automations.yaml:/config/automations.yaml:ro
|
||||||
|
- /mnt/user/services/smart-home-kalli/home-assistant/scripts.yaml:/config/scripts.yaml:ro
|
||||||
|
- /mnt/user/services/smart-home-kalli/home-assistant/scenes.yaml:/config/scenes.yaml:ro
|
||||||
|
- /mnt/user/services/smart-home-kalli/home-assistant/packages:/config/packages:ro
|
||||||
|
networks:
|
||||||
|
- frontend_net
|
||||||
|
- smarthome_net
|
||||||
|
expose:
|
||||||
|
- "8123"
|
||||||
|
security_opt:
|
||||||
|
- no-new-privileges:true
|
||||||
|
depends_on:
|
||||||
|
- mosquitto
|
||||||
|
labels:
|
||||||
|
- traefik.enable=true
|
||||||
|
- traefik.docker.network=frontend_net
|
||||||
|
- traefik.http.routers.homeassistant.rule=Host(`home.kaleschke.info`)
|
||||||
|
- traefik.http.routers.homeassistant.entrypoints=websecure
|
||||||
|
- traefik.http.routers.homeassistant.tls=true
|
||||||
|
- traefik.http.routers.homeassistant.tls.certresolver=le
|
||||||
|
# Temporary onboarding guard: remove after the HA owner account exists.
|
||||||
|
- traefik.http.routers.homeassistant.middlewares=authelia@file,secure-headers@file
|
||||||
|
- traefik.http.services.homeassistant.loadbalancer.server.port=8123
|
||||||
|
|
||||||
|
mosquitto:
|
||||||
|
image: eclipse-mosquitto:2.0.22@sha256:914f529386804c8278a4e581526b9be5e1604df44b30daabc70aa97dcefe5268
|
||||||
|
container_name: smarthome-mosquitto
|
||||||
|
restart: unless-stopped
|
||||||
|
volumes:
|
||||||
|
- ./mosquitto/config/mosquitto.conf:/mosquitto/config/mosquitto.conf
|
||||||
|
- /mnt/user/appdata/mosquitto/config:/mosquitto/external_config
|
||||||
|
- /mnt/user/appdata/mosquitto/data:/mosquitto/data
|
||||||
|
- /mnt/user/appdata/mosquitto/log:/mosquitto/log
|
||||||
|
networks:
|
||||||
|
- smarthome_net
|
||||||
|
expose:
|
||||||
|
- "1883"
|
||||||
|
security_opt:
|
||||||
|
- no-new-privileges:true
|
||||||
|
|
||||||
|
networks:
|
||||||
|
frontend_net:
|
||||||
|
external: true
|
||||||
|
|
||||||
|
smarthome_net:
|
||||||
|
name: smarthome_net
|
||||||
|
driver: bridge
|
||||||
|
internal: true
|
||||||
@@ -0,0 +1,15 @@
|
|||||||
|
per_listener_settings true
|
||||||
|
|
||||||
|
listener 1883 0.0.0.0
|
||||||
|
allow_anonymous false
|
||||||
|
password_file /mosquitto/external_config/passwordfile
|
||||||
|
acl_file /mosquitto/external_config/aclfile
|
||||||
|
|
||||||
|
persistence true
|
||||||
|
persistence_location /mosquitto/data/
|
||||||
|
|
||||||
|
log_dest stdout
|
||||||
|
log_type error
|
||||||
|
log_type warning
|
||||||
|
log_type notice
|
||||||
|
connection_messages true
|
||||||
Reference in New Issue
Block a user