From 31d653ddb57791386699d938566df4876eadf98f Mon Sep 17 00:00:00 2001 From: Luke Else Date: Mon, 27 Jul 2026 19:56:44 +0100 Subject: [PATCH] f --- infra/main.tf | 1 + readme.md | 4 +- services/prod/home-docker-compose.yml | 26 ++++++++++ services/prod/home/config/docker.yaml | 6 +++ services/prod/home/config/services.yaml | 63 +++++++++++++++++++++++++ services/prod/home/config/settings.yaml | 14 ++++++ services/prod/home/config/widgets.yaml | 9 ++++ services/prod/spindown.sh | 1 + services/prod/spinup.sh | 1 + 9 files changed, 124 insertions(+), 1 deletion(-) create mode 100644 services/prod/home-docker-compose.yml create mode 100644 services/prod/home/config/docker.yaml create mode 100644 services/prod/home/config/services.yaml create mode 100644 services/prod/home/config/settings.yaml create mode 100644 services/prod/home/config/widgets.yaml diff --git a/infra/main.tf b/infra/main.tf index 1b268c4..210ec4f 100644 --- a/infra/main.tf +++ b/infra/main.tf @@ -26,6 +26,7 @@ locals { { zone = "luke-else.co.uk", name = "status", value = module.prod.ipv4 }, { zone = "luke-else.co.uk", name = "bitwarden", value = module.prod.ipv4 }, { zone = "luke-else.co.uk", name = "rd", value = module.prod.ipv4 }, + { zone = "luke-else.co.uk", name = "home", value = module.prod.ipv4 }, { zone = "luke-else.co.uk", name = "git", value = module.dev.ipv4 }, { zone = "luke-else.co.uk", name = "traefik.cicd", value = module.dev.ipv4 }, { zone = "luke-else.co.uk", name = "vpn", value = module.vpn.ipv4 }, diff --git a/readme.md b/readme.md index e28ba81..64565a1 100644 --- a/readme.md +++ b/readme.md @@ -85,7 +85,7 @@ architecture-beta │ └── playbooks/ # bootstrap.yml, deploy.yml, spinup.yml, spindown.yml, site.yml ├── services/ # Docker Compose stacks, grouped by which server they run on │ ├── dev/ # Gitea + CI runners + Traefik + backup/restore -│ ├── prod/ # Websites, Bitwarden, RustDesk, status page + Traefik + backup/restore +│ ├── prod/ # Websites, Bitwarden, RustDesk, status page, home dashboard + Traefik + backup/restore │ └── vpn/ # WireGuard (wg-easy) + Traefik + backup/restore ├── .devcontainer/ # git submodule: shared devcontainer for this repo (OpenTofu tooling) └── assets/ @@ -101,6 +101,7 @@ Each of `services/dev`, `services/prod`, `services/vpn` follows the same convent - **DNS is provisioned, not just documented.** `module.dns` creates a Hetzner zone and every A/MX/TXT/CNAME record in `infra/main.tf` — see [Managing DNS](#managing-dns). - **Hosts are discovered dynamically** from the Hetzner API by role label, not a static inventory file — nothing in Ansible reads Terraform state. - **Gitea Actions runners are disposable.** Their `/data` is a local cache; runner identity re-registers with Gitea on every `spinup.sh`, so they're excluded from backup. +- **The `home` dashboard only gets Docker container widgets for `prod`.** It links to every service on all three hosts, but container-level widgets (CPU/memory) are wired up only for containers on `prod` itself (`services/prod/home/config/docker.yaml`) — reaching into `dev`'s or `vpn`'s Docker socket would mean opening a path across hosts, which cuts against `vpn`'s deliberate isolation above. The Traefik widgets for `dev`/`vpn` are the one exception: they hit those hosts' public, unauthenticated dashboard URLs (same as any browser could), not their Docker sockets, so they need no network changes. ## Prerequisites @@ -224,6 +225,7 @@ All three hosts run [Watchtower](https://containrrr.dev/watchtower/), polling ev | Status page (Uptime Kuma) | `status-docker-compose.yml` | `status.luke-else.co.uk` | | Bitwarden (Vaultwarden) | `bitwarden-docker-compose.yml` | `bitwarden.luke-else.co.uk` | | RustDesk relay (hbbs/hbbr) | `rd-docker-compose.yml` | `rd.luke-else.co.uk`, ports `21115-21119` | +| Home dashboard (Homepage) | `home-docker-compose.yml` | `home.luke-else.co.uk` | | Watchtower | `watchtower-docker-compose.yml` | — | | Backup/restore | `backup-docker-compose.yml` | — | diff --git a/services/prod/home-docker-compose.yml b/services/prod/home-docker-compose.yml new file mode 100644 index 0000000..fce506e --- /dev/null +++ b/services/prod/home-docker-compose.yml @@ -0,0 +1,26 @@ +services: + home: + image: ghcr.io/gethomepage/homepage:latest + container_name: home + volumes: + - ./home/config:/app/config + - /var/run/docker.sock:/var/run/docker.sock:ro + networks: + - proxy + labels: + ## Expose home Through Trefik ## + - "traefik.enable=true" # <== Enable traefik to proxy this container + + - "traefik.http.middlewares.redirect-web-secure.redirectscheme.scheme=https" + - "traefik.http.routers.home-insecure.middlewares=redirect-web-secure" + - "traefik.http.routers.home-insecure.rule=Host(`home.luke-else.co.uk`)" + - "traefik.http.routers.home-insecure.entrypoints=web" + + - "traefik.http.routers.home.rule=Host(`home.luke-else.co.uk`)" + - "traefik.http.routers.home.entrypoints=websecure" + - "traefik.http.routers.home.tls.certresolver=myresolver" + restart: unless-stopped + +networks: + proxy: + external: true diff --git a/services/prod/home/config/docker.yaml b/services/prod/home/config/docker.yaml new file mode 100644 index 0000000..4ad0afc --- /dev/null +++ b/services/prod/home/config/docker.yaml @@ -0,0 +1,6 @@ +# Docker host home reads live container status from - only ever the local +# socket for prod. dev/vpn are separate hosts with their own Docker daemons +# and are deliberately not reachable from here (see architecture diagram in +# the README), so their services are plain links, not live widgets. +my-docker: + socket: /var/run/docker.sock diff --git a/services/prod/home/config/services.yaml b/services/prod/home/config/services.yaml new file mode 100644 index 0000000..5bb2415 --- /dev/null +++ b/services/prod/home/config/services.yaml @@ -0,0 +1,63 @@ +- Websites: + - luke-else.co.uk: + href: https://luke-else.co.uk + description: Personal site (prod) + icon: mdi-web + container: luke-else + +- Services: + - Status page: + href: https://status.luke-else.co.uk + description: Uptime Kuma + icon: mdi-list-status + container: status + widget: + type: uptimekuma + url: https://status.luke-else.co.uk + slug: dashboard + - Bitwarden: + href: https://bitwarden.luke-else.co.uk + description: Vaultwarden + icon: mdi-key-variant + container: bitwarden + - RustDesk: + href: https://rd.luke-else.co.uk + description: RustDesk relay (hbbs/hbbr) + icon: mdi-remote-desktop + container: rustdesk-hbbs + - Gitea: + href: https://git.luke-else.co.uk + description: Git hosting + CI (dev) + icon: mdi-git + - WireGuard: + href: https://vpn.luke-else.co.uk + description: wg-easy (vpn) + icon: mdi-vpn + +- Infrastructure: + - Traefik (prod): + href: https://traefik.luke-else.co.uk + description: Reverse proxy - prod + icon: mdi-lan-connect + container: traefik + widget: + type: traefik + url: http://traefik.luke-else.co.uk + - Traefik (dev): + href: https://traefik.cicd.luke-else.co.uk + description: Reverse proxy - dev + icon: mdi-lan-connect + widget: + type: traefik + url: https://traefik.cicd.luke-else.co.uk + - Traefik (vpn): + href: https://traefik.vpn.luke-else.co.uk + description: Reverse proxy - vpn + icon: mdi-lan-connect + widget: + type: traefik + url: https://traefik.vpn.luke-else.co.uk + - Hetzner Cloud Console: + href: https://console.hetzner.cloud/ + description: Servers, networks, firewalls (OpenTofu-managed) + icon: mdi-cloud-outline diff --git a/services/prod/home/config/settings.yaml b/services/prod/home/config/settings.yaml new file mode 100644 index 0000000..0a1ba88 --- /dev/null +++ b/services/prod/home/config/settings.yaml @@ -0,0 +1,14 @@ +title: Home +theme: dark +color: slate +headerStyle: boxed +layout: + Websites: + style: row + columns: 3 + Services: + style: row + columns: 3 + Infrastructure: + style: row + columns: 3 diff --git a/services/prod/home/config/widgets.yaml b/services/prod/home/config/widgets.yaml new file mode 100644 index 0000000..798bc09 --- /dev/null +++ b/services/prod/home/config/widgets.yaml @@ -0,0 +1,9 @@ +# Info bar widgets - stats for *this* host (prod), where home actually runs. +- resources: + label: prod + cpu: true + memory: true + disk: / +- search: + provider: duckduckgo + target: _blank diff --git a/services/prod/spindown.sh b/services/prod/spindown.sh index e6c19ad..5827056 100755 --- a/services/prod/spindown.sh +++ b/services/prod/spindown.sh @@ -6,6 +6,7 @@ docker compose -f web-docker-compose.yml down docker compose -f status-docker-compose.yml down docker compose -f bitwarden-docker-compose.yml down docker compose -f rd-docker-compose.yml down +docker compose -f home-docker-compose.yml down docker compose -f watchtower-docker-compose.yml down docker compose -f traefik-docker-compose.yml down docker compose -f backup-docker-compose.yml down diff --git a/services/prod/spinup.sh b/services/prod/spinup.sh index ac5092b..3d4b377 100755 --- a/services/prod/spinup.sh +++ b/services/prod/spinup.sh @@ -21,6 +21,7 @@ docker compose -f status-docker-compose.yml pull && docker compose -f status-doc docker compose -f web-docker-compose.yml pull && docker compose -f web-docker-compose.yml up -d docker compose -f bitwarden-docker-compose.yml pull && docker compose -f bitwarden-docker-compose.yml up -d docker compose -f rd-docker-compose.yml pull && docker compose -f rd-docker-compose.yml up -d +docker compose -f home-docker-compose.yml pull && docker compose -f home-docker-compose.yml up -d docker compose -f backup-docker-compose.yml pull backup docker compose -f backup-docker-compose.yml up -d backup