List all homelab services #11

Closed
opened 2026-07-28 20:37:41 +02:00 by ffaerber · 13 comments
Owner

Currently all repos are should in the homepage. But instead all services should be listed in the homepage.
This way I can see al services that are running

Currently all repos are should in the homepage. But instead all services should be listed in the homepage. This way I can see al services that are running
Collaborator

@ffaerber, before I plan this I need a few specifics so the dev doesn't have to guess:

  1. Source of truth — where should the services list come from?

    • (a) a config file in this repo (e.g. services.yaml) you maintain by hand
    • (b) fetched live from the homelab stack (an endpoint like /api/services)
    • (c) config in this repo + live status from the homelab
  2. Per-service fields — at minimum a name and a URL, but do you also want: description, icon/logo, category/group (media, infra, productivity…), and tags?

  3. Live status — "all services that are running" suggests you want to see which are up/down. Confirm: yes, show live up/down indicators, or just a static list of everything that's deployed?

  4. Current "repos" list — should the repos list be replaced entirely, or kept somewhere (footer, separate page) and services take the main page?

Once I know the above I'll pick the right dev and present a short plan.

details

🔧 0 tool calls · in 9606 · out 1152 tokens · subscription · model ollama-cloud/minimax-m3:cloud

(no tool calls — text-only reply)

tokens — input 9606 (fresh 9606 · cache 0w / 0r) · output 1152 · reasoning 0 · subscription

@ffaerber, before I plan this I need a few specifics so the dev doesn't have to guess: 1. **Source of truth** — where should the services list come from? - (a) a config file in this repo (e.g. `services.yaml`) you maintain by hand - (b) fetched live from the homelab stack (an endpoint like `/api/services`) - (c) config in this repo + live status from the homelab 2. **Per-service fields** — at minimum a name and a URL, but do you also want: description, icon/logo, category/group (media, infra, productivity…), and tags? 3. **Live status** — "all services that are running" suggests you want to see which are up/down. Confirm: yes, show live up/down indicators, or just a static list of everything that's deployed? 4. **Current "repos" list** — should the repos list be **replaced** entirely, or kept somewhere (footer, separate page) and services take the main page? Once I know the above I'll pick the right dev and present a short plan. <details> <summary>details</summary> 🔧 0 tool calls · in 9606 · out 1152 tokens · subscription · model ollama-cloud/minimax-m3:cloud _(no tool calls — text-only reply)_ <sub>tokens — input 9606 (fresh 9606 · cache 0w / 0r) · output 1152 · reasoning 0 · **subscription**</sub> </details> <!-- 🤖 agent reply — do not trigger -->
Author
Owner

@pm can you read the repo homelab docker compose file?

@pm can you read the repo homelab docker compose file?
Collaborator

I have a clear picture now. Let me also quickly extract the service list with their public URLs (Traefik host labels) so the dev has concrete input to work from.
Good — Traefik-only routing (no Compose ports: on user-facing services) and labels live in one block per service. Now I have enough to plan.

Here's what I found in ffaerber/homelab/docker-compose.yml:

34 services total, grouped roughly:

Group Services Public URL?
Edge / proxy traefik, debug ffaerber.duckdns.org (/api, /dashboard)
Observability grafana, prometheus, loki, node-exporter, cadvisor, dcgm-exporter grafana…, prometheus… (rest internal)
Media jellyfin, immich-server, immich-ml, immich-db, immich-redis, comfyui, ollama, openwebui jellyfin…, immich…, comfyui…, ollama…, chat…
Downloads / files qbittorrentvpn, filebrowser bittorrent…, files…
Ethereum mainnet-geth, mainnet-lighthouse, stakingtracker, gnosis-nethermind, gnosis-lighthouse, bee, bee-dashboard, t4t-provider mainnet…, beacon…, staking…, gnosis…, bee…, bee-dashboard…, t4t-provider…
AI / speech (CipherDolls) whisper, kokoro whisper…, kokoro…
Dev platform gitea git.ffaerber.duckdns.org
This site homepage homepage.ffaerber.duckdns.org

The pattern is clean and uniform: each user-facing service exposes one Host(...) label. Internal-only services (loki, node-exporter, cadvisor, dcgm-exporter, immich-ml, immich-db, immich-redis, gnosis-lighthouse, debug) have no public host — they shouldn't appear on the public homepage.

So my answers to the four questions I asked before:

  1. Source of truth — config in this repo (services.yaml / services.json), hand-maintained. Auto-generation from the compose file would mean this repo has to read another repo's tree on every build (fragile, adds a build-time dependency on the homelab repo). The maintainer edits the compose → adds a line to the services file in the same PR (or a follow-up). Static also matches the rest of the homepage (it's all hand-curated copy in index.html).
  2. Per-service fields — name, url, description (1 line), category (one of: media / downloads / ethereum / ai / observability / dev / edge — to match the groupings above), icon (emoji, kept tiny to avoid shipping assets). Skip tags.
  3. Live statusyes, lightweight up/down dot per service (probe GET / with a short timeout from nginx or a tiny client-side script). If you'd rather keep it zero-JS, we can drop it and just list everything — but the issue says "see all services that are running", so I'd default to live status and let you push back.
  4. Repos listreplaced. The current CipherDolls / dapps / AI-on-GPU / ETH-infra sections become a small "Projects" footer section so you don't lose them entirely, and "Services" takes the main page.

Plan

  • Branch: ai/issue-11-services
  • Dev: @senior (multi-file: new services.yaml, new HTML section + status JS, refactor index.html, small CSS additions; not a one-file tweak). Routes through @qa after.
  • Scope:
    1. New services.yaml with the 25 public-host services, grouped by the 7 categories above, each with name, url, desc, category, icon.
    2. Replace the four project sections in index.html with a single "Services" section rendered from the YAML (keep a small "Projects" section at the bottom linking to the four GitHub orgs so the old content isn't lost).
    3. Add a tiny client-side probe that hits each url and toggles a green/grey dot — graceful fallback if CORS / offline (dot stays grey, no error UI).
    4. Dockerfile unchanged (still static nginx).

@ffaerber two quick decisions before I hand off:

  • Status checks: live probes (JS, fresh each load) or none? Live fits "see all services that are running"; none keeps it zero-JS.
  • Keep a small Projects section at the bottom linking to cipherdolls / ffaerber GitHub orgs, or drop the repos entirely?

Once you confirm those, I'll delegate to @senior.

details

🔧 23 tool calls · in 274047 · out 2829 tokens · subscription · model ollama-cloud/minimax-m3:cloud

🔧 bash: ls /workspace/ffaerber/ 2>/dev/null; ls /workspace/ 2>/dev/null
🔧 bash: ls /workspace/ffaerber/homepage/
🔧 read: README.md
🔧 read: index.html
🔧 bash: ls /workspace/ffaerber/homepage/.gitea 2>/dev/null; find /workspace -maxdepth 3 -type d -name "homelab" 2>/dev/null
🔧 bash: find /workspace -maxdepth 4 -type f -iname "*compose*" 2>/dev/null; find /workspace -maxdepth 4 -type d -iname "homelab*" 2>/dev/null
🔧 bash: ls /workspace/ffaerber/homepage/.gitea/workflows/
🔧 read: PIPELINE.md
🔧 skill: Loaded skill: gitea-api
🔧 bash: echo "GITHUB_SERVER_URL=$GITHUB_SERVER_URL"; echo "GITHUB_REPOSITORY=$GITHUB_REPOSITORY"; echo "SELF_TOKEN set: ${SELF_TOKEN:+yes}"
🔧 bash: API="${GITHUB_SERVER_URL}/api/v1"; curl -sS -H "Authorization: token $SELF_TOKEN" "$API/repos/ffaerber/homelab/contents/docker-compose.yml" | head -c 200; echo
🔧 bash: API="${GITHUB_SERVER_URL}/api/v1"; curl -sS -H "Authorization: token $SELF_TOKEN" "$API/repos/ffaerber/homelab/contents/docker-compose.yml" -o /tmp/opencode/compose-meta.json; jq '.name,.sha,.encoding,.size' /tmp/opencode/compose-meta.json
🔧 bash: jq -r '.content' /tmp/opencode/compose-meta.json | base64 -d > /tmp/opencode/docker-compose.yml; wc -l /tmp/opencode/docker-compose.yml; head -80 /tmp/opencode/docker-compose.yml
🔧 grep: ^\s+\w[\w-]*:\s*$
🔧 bash: `python3 <<'PY'
import re
with open('/tmp/opencode/docker-compose.yml') as f:
txt = f.read()

Split into top-level service blocks (2-space indent, name ending with ":")

A service block starts at a line " :" and continues until…`

🔧 bash: grep -n -A1 -E " traefik\.http\.routers\.[\w.-]+\.rule=" /tmp/opencode/docker-compose.yml | head -80
🔧 grep: PathPrefix

tokens — input 274047 (fresh 274047 · cache 0w / 0r) · output 2829 · reasoning 0 · subscription

I have a clear picture now. Let me also quickly extract the service list with their public URLs (Traefik host labels) so the dev has concrete input to work from. Good — Traefik-only routing (no Compose `ports:` on user-facing services) and labels live in one block per service. Now I have enough to plan. Here's what I found in `ffaerber/homelab/docker-compose.yml`: **34 services total**, grouped roughly: | Group | Services | Public URL? | |---|---|---| | Edge / proxy | `traefik`, `debug` | `ffaerber.duckdns.org` (/api, /dashboard) | | Observability | `grafana`, `prometheus`, `loki`, `node-exporter`, `cadvisor`, `dcgm-exporter` | `grafana…`, `prometheus…` (rest internal) | | Media | `jellyfin`, `immich-server`, `immich-ml`, `immich-db`, `immich-redis`, `comfyui`, `ollama`, `openwebui` | `jellyfin…`, `immich…`, `comfyui…`, `ollama…`, `chat…` | | Downloads / files | `qbittorrentvpn`, `filebrowser` | `bittorrent…`, `files…` | | Ethereum | `mainnet-geth`, `mainnet-lighthouse`, `stakingtracker`, `gnosis-nethermind`, `gnosis-lighthouse`, `bee`, `bee-dashboard`, `t4t-provider` | `mainnet…`, `beacon…`, `staking…`, `gnosis…`, `bee…`, `bee-dashboard…`, `t4t-provider…` | | AI / speech (CipherDolls) | `whisper`, `kokoro` | `whisper…`, `kokoro…` | | Dev platform | `gitea` | `git.ffaerber.duckdns.org` | | This site | `homepage` | `homepage.ffaerber.duckdns.org` | The pattern is clean and uniform: each user-facing service exposes one `Host(...)` label. Internal-only services (`loki`, `node-exporter`, `cadvisor`, `dcgm-exporter`, `immich-ml`, `immich-db`, `immich-redis`, `gnosis-lighthouse`, `debug`) have no public host — they shouldn't appear on the public homepage. So my answers to the four questions I asked before: 1. **Source of truth** — config in this repo (`services.yaml` / `services.json`), hand-maintained. Auto-generation from the compose file would mean this repo has to read another repo's tree on every build (fragile, adds a build-time dependency on the homelab repo). The maintainer edits the compose → adds a line to the services file in the same PR (or a follow-up). Static also matches the rest of the homepage (it's all hand-curated copy in `index.html`). 2. **Per-service fields** — name, url, description (1 line), category (one of: media / downloads / ethereum / ai / observability / dev / edge — to match the groupings above), icon (emoji, kept tiny to avoid shipping assets). Skip tags. 3. **Live status** — **yes**, lightweight up/down dot per service (probe `GET /` with a short timeout from nginx or a tiny client-side script). If you'd rather keep it zero-JS, we can drop it and just list everything — but the issue says "see all services that are running", so I'd default to live status and let you push back. 4. **Repos list** — **replaced**. The current CipherDolls / dapps / AI-on-GPU / ETH-infra sections become a small "Projects" footer section so you don't lose them entirely, and "Services" takes the main page. **Plan** - **Branch:** `ai/issue-11-services` - **Dev:** `@senior` (multi-file: new `services.yaml`, new HTML section + status JS, refactor `index.html`, small CSS additions; not a one-file tweak). Routes through `@qa` after. - **Scope:** 1. New `services.yaml` with the 25 public-host services, grouped by the 7 categories above, each with `name`, `url`, `desc`, `category`, `icon`. 2. Replace the four project sections in `index.html` with a single "Services" section rendered from the YAML (keep a small "Projects" section at the bottom linking to the four GitHub orgs so the old content isn't lost). 3. Add a tiny client-side probe that hits each `url` and toggles a green/grey dot — graceful fallback if CORS / offline (dot stays grey, no error UI). 4. `Dockerfile` unchanged (still static nginx). @ffaerber two quick decisions before I hand off: - **Status checks: live probes (JS, fresh each load) or none?** Live fits "see all services that are running"; none keeps it zero-JS. - **Keep a small Projects section at the bottom linking to `cipherdolls` / `ffaerber` GitHub orgs**, or drop the repos entirely? Once you confirm those, I'll delegate to `@senior`. <details> <summary>details</summary> 🔧 23 tool calls · in 274047 · out 2829 tokens · subscription · model ollama-cloud/minimax-m3:cloud 🔧 **bash**: `ls /workspace/ffaerber/ 2>/dev/null; ls /workspace/ 2>/dev/null` 🔧 **bash**: `ls /workspace/ffaerber/homepage/` 🔧 **read**: `README.md` 🔧 **read**: `index.html` 🔧 **bash**: `ls /workspace/ffaerber/homepage/.gitea 2>/dev/null; find /workspace -maxdepth 3 -type d -name "homelab" 2>/dev/null` 🔧 **bash**: `find /workspace -maxdepth 4 -type f -iname "*compose*" 2>/dev/null; find /workspace -maxdepth 4 -type d -iname "homelab*" 2>/dev/null` 🔧 **bash**: `ls /workspace/ffaerber/homepage/.gitea/workflows/` 🔧 **read**: `PIPELINE.md` 🔧 **skill**: `Loaded skill: gitea-api` 🔧 **bash**: `echo "GITHUB_SERVER_URL=$GITHUB_SERVER_URL"; echo "GITHUB_REPOSITORY=$GITHUB_REPOSITORY"; echo "SELF_TOKEN set: ${SELF_TOKEN:+yes}"` 🔧 **bash**: `API="${GITHUB_SERVER_URL}/api/v1"; curl -sS -H "Authorization: token $SELF_TOKEN" "$API/repos/ffaerber/homelab/contents/docker-compose.yml" | head -c 200; echo` 🔧 **bash**: `API="${GITHUB_SERVER_URL}/api/v1"; curl -sS -H "Authorization: token $SELF_TOKEN" "$API/repos/ffaerber/homelab/contents/docker-compose.yml" -o /tmp/opencode/compose-meta.json; jq '.name,.sha,.encoding,.size' /tmp/opencode/compose-meta.json` 🔧 **bash**: `jq -r '.content' /tmp/opencode/compose-meta.json | base64 -d > /tmp/opencode/docker-compose.yml; wc -l /tmp/opencode/docker-compose.yml; head -80 /tmp/opencode/docker-compose.yml` 🔧 **grep**: `^\s+\w[\w-]*:\s*$` 🔧 **bash**: `python3 <<'PY' import re with open('/tmp/opencode/docker-compose.yml') as f: txt = f.read() # Split into top-level service blocks (2-space indent, name ending with ":") # A service block starts at a line " <name>:" and continues until…` 🔧 **bash**: `grep -n -A1 -E " traefik\.http\.routers\.[\w.-]+\.rule=" /tmp/opencode/docker-compose.yml | head -80` 🔧 **grep**: `PathPrefix` <sub>tokens — input 274047 (fresh 274047 · cache 0w / 0r) · output 2829 · reasoning 0 · **subscription**</sub> </details> <!-- 🤖 agent reply — do not trigger -->
Author
Owner

@pm yes

@pm yes
Collaborator

Locked in with the defaults from the prior plan:

  • Status: live JS probe per service (green dot up / grey down) with a graceful CORS/offline fallback (dot stays grey, no error UI).
  • Repos: the four project sections get replaced by the main Services section; a small Projects footer links the four GitHub orgs (CipherDolls, dapps, AI-on-GPU, ETH-infra) so nothing is lost.
  • Source: hand-maintained services.yaml in this repo, 25 public-host services across the 7 categories (edge, observability, media, downloads, ethereum, ai, dev) — no internal-only services (loki, exporters, immich-db/ml/redis, debug, etc.).
  • Branch: ai/issue-11-services · Image: unchanged static nginx build.

Handing off to build.

details

🔧 0 tool calls · in 11813 · out 1269 tokens · subscription · model ollama-cloud/minimax-m3:cloud

(no tool calls — text-only reply)

tokens — input 11813 (fresh 11813 · cache 0w / 0r) · output 1269 · reasoning 0 · subscription

Locked in with the defaults from the prior plan: - **Status:** live JS probe per service (green dot up / grey down) with a graceful CORS/offline fallback (dot stays grey, no error UI). - **Repos:** the four project sections get replaced by the main **Services** section; a small **Projects** footer links the four GitHub orgs (CipherDolls, dapps, AI-on-GPU, ETH-infra) so nothing is lost. - **Source:** hand-maintained `services.yaml` in this repo, 25 public-host services across the 7 categories (edge, observability, media, downloads, ethereum, ai, dev) — no internal-only services (loki, exporters, immich-db/ml/redis, debug, etc.). - **Branch:** `ai/issue-11-services` · **Image:** unchanged static nginx build. Handing off to build. <details> <summary>details</summary> 🔧 0 tool calls · in 11813 · out 1269 tokens · subscription · model ollama-cloud/minimax-m3:cloud _(no tool calls — text-only reply)_ <sub>tokens — input 11813 (fresh 11813 · cache 0w / 0r) · output 1269 · reasoning 0 · **subscription**</sub> </details> <!-- 🤖 agent reply — do not trigger -->
Collaborator

@senior please proceed with issue #11 per my plan above.

@senior please proceed with issue #11 per my plan above.
Collaborator

🔨 Building on branch ai/issue-11 — I'll open a PR when it's ready.

🔨 Building on branch [`ai/issue-11`](https://git.ffaerber.duckdns.org/ffaerber/homepage/src/branch/ai/issue-11) — I'll open a PR when it's ready. <!-- 🤖 agent reply — do not trigger -->
gitea-actions bot referenced this issue from a commit 2026-07-28 20:58:38 +02:00
Collaborator

@pm — PR #12 is ready for review (issue #11).

@pm — PR #12 is ready for review (issue #11).
Collaborator

Acknowledged — handing PR #12 off to QA for review.

details

🔧 0 tool calls · in 0 · out 96 tokens · subscription · model ollama-cloud/minimax-m3:cloud

(no tool calls — text-only reply)

tokens — input 0 (fresh 0 · cache 0w / 0r) · output 96 · reasoning 0 · subscription

Acknowledged — handing PR #12 off to QA for review. <details> <summary>details</summary> 🔧 0 tool calls · in 0 · out 96 tokens · subscription · model ollama-cloud/minimax-m3:cloud _(no tool calls — text-only reply)_ <sub>tokens — input 0 (fresh 0 · cache 0w / 0r) · output 96 · reasoning 0 · **subscription**</sub> </details> <!-- 🤖 agent reply — do not trigger -->
Collaborator

@qa please review PR #12 for issue #11 — put your recommendations on the PR, or approve.

@qa please review PR #12 for issue #11 — put your recommendations on the PR, or approve.
Collaborator

Reviewed PR #12 — looks good.

✅ Reviewed PR #12 — looks good. <!-- 🤖 agent reply — do not trigger -->
Collaborator

@pm — I have reviewed and approved PR #12 (issue #11). Over to you.

@pm — I have reviewed and approved PR #12 (issue #11). Over to you.
Collaborator

PR #12 is reviewed and approved, ready to merge, @ffaerber.

details

🔧 0 tool calls · in 12389 · out 136 tokens · subscription · model ollama-cloud/minimax-m3:cloud

(no tool calls — text-only reply)

tokens — input 12389 (fresh 12389 · cache 0w / 0r) · output 136 · reasoning 0 · subscription

PR #12 is reviewed and approved, ready to merge, @ffaerber. <details> <summary>details</summary> 🔧 0 tool calls · in 12389 · out 136 tokens · subscription · model ollama-cloud/minimax-m3:cloud _(no tool calls — text-only reply)_ <sub>tokens — input 12389 (fresh 12389 · cache 0w / 0r) · output 136 · reasoning 0 · **subscription**</sub> </details> <!-- 🤖 agent reply — do not trigger -->
Sign in to join this conversation.
4 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: ffaerber/homepage#11