trmnl (128): provisioned — container page, inventory, ingress
LXC 128 trmnl hosts the TRMNL plugins middleware (dtoro/terminalito), polled by TRMNL cloud. trmnl-plugins.service on :9851; Caddy block + LE cert; VPS traefik router trmnl-public + cert mirror. Public path pending VPS<->home netbird route recovery (was "No networks available" at provision time, artifacto/blog 504 too). LAN Technitium record + SOPS enrollment + Google/MVG creds pending. Plan -> In Progress. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
42
containers/128-trmnl.md
Normal file
42
containers/128-trmnl.md
Normal file
@@ -0,0 +1,42 @@
|
|||||||
|
# 128 — `trmnl`
|
||||||
|
|
||||||
|
Self-hosted middleware for TRMNL e-ink plugins. TRMNL cloud polls it; it fetches/shapes live data into JSON the plugin's Liquid template renders.
|
||||||
|
|
||||||
|
## At a glance
|
||||||
|
- **Hostname:** `trmnl`
|
||||||
|
- **IP:** `192.168.8.211`
|
||||||
|
- **Privilege:** unprivileged
|
||||||
|
- **Resources:** 1 core / 768 MiB RAM / 8 GiB rootfs (Debian 13)
|
||||||
|
- **Mounts:** none
|
||||||
|
- **Public hostname:** `trmnl.hubris.network` (via [VPS ingress](../infrastructure/ingress.md))
|
||||||
|
|
||||||
|
## Role
|
||||||
|
Runs one FastAPI aggregator (`server.app:app`, port 9851) that mounts a router per plugin from the `dtoro/terminalito` repo. First consumer: `munich-home` (`/munich-home/dashboard`) — weather (Open-Meteo), MVG transit, Google Calendar, plus server-side Kita/quote logic. Talks out to the public internet for those APIs; TRMNL cloud polls it inbound every 15 min. Bearer-token gated (`TRMNL_POLL_TOKEN`); `/health` is open.
|
||||||
|
|
||||||
|
## Service / port map
|
||||||
|
| Service | Listen | Notes |
|
||||||
|
|---------|--------|-------|
|
||||||
|
| `trmnl-plugins` | `0.0.0.0:9851` | uvicorn aggregator; `EnvironmentFile=/etc/trmnl-plugins/env` |
|
||||||
|
|
||||||
|
## Storage / config paths
|
||||||
|
- `/opt/terminalito` — git checkout (origin = internal gitea `http://192.168.8.121:3000/dtoro/terminalito.git`)
|
||||||
|
- `/opt/terminalito/server/.venv` — venv
|
||||||
|
- `/etc/trmnl-plugins/env` — `TRMNL_POLL_TOKEN` (+ Google/MVG creds once enrolled)
|
||||||
|
- `/etc/systemd/system/trmnl-plugins.service`
|
||||||
|
|
||||||
|
## Auto-deploy
|
||||||
|
Not yet wired. Manual deploy: `pct exec 128 -- /opt/terminalito/server/deploy/deploy.sh`. When wired it follows [auto-deploy](../infrastructure/auto-deploy.md) Shape B (`/opt/terminalito-deploy/`, gitea webhook on `dtoro/terminalito`, add `192.168.8.211` to gitea `ALLOWED_HOST_LIST`).
|
||||||
|
|
||||||
|
## Secrets
|
||||||
|
Not yet SOPS-enrolled. The poll token is set directly in `/etc/trmnl-plugins/env`. Google Calendar + MVG creds are pending: enroll via `homelab client add trmnl` + bootstrap, add `secrets/trmnl-oauth.yaml`, then `server/deploy/render-env.sh` builds the env from `homelab secret trmnl-oauth`. Until then calendar/transit cards degrade to empty; weather works.
|
||||||
|
|
||||||
|
## Related
|
||||||
|
- [Caddy (121)](121-caddy.md) — LAN reverse proxy (`trmnl.hubris.network → 192.168.8.211:9851`)
|
||||||
|
- [VPS ingress](../infrastructure/ingress.md) — public edge (cert mirror + traefik router)
|
||||||
|
- [DNS (107)](107-dns.md) — needs Technitium A record `trmnl → 192.168.8.175` (pending)
|
||||||
|
- [Gitea (104)](104-gitea.md) — source repo `dtoro/terminalito`
|
||||||
|
- [Plan: 2026-06-24 TRMNL plugins LXC](../plans/2026-06-24-trmnl-plugins-lxc.md)
|
||||||
|
|
||||||
|
## Changelog
|
||||||
|
### 2026-06-24 — provisioned
|
||||||
|
LXC 128 created (Debian 13, unprivileged, `192.168.8.211`). Deployed `trmnl-plugins.service` on :9851 from `dtoro/terminalito`. Caddy block added (`dtoro/caddy-conf`) + LE cert via IONOS DNS-01; verified `/health` 200 and `/munich-home/dashboard` (live weather) through Caddy. Cert mirrored to VPS (`trmnl.fullchain.crt`/`trmnl.privkey.key`) + traefik router `trmnl-public` → `192.168.8.211:9851` added to `/opt/traefik-dynamic.yaml`. **Public path pending**: VPS↔home netbird route was down at provision time (`No networks available`, 3/6 peers — artifacto/blog also 504); resolves when the mesh route recovers. **LAN pending**: Technitium A record not yet added. Not SOPS-enrolled; Google/MVG creds pending.
|
||||||
@@ -16,6 +16,7 @@ All containers live on [`hubris`](../hosts/hubris.md). Each row links to the per
|
|||||||
| 122 | [arriman](122-arriman.md) | 192.168.8.132 | priv | 4 | 8 GiB | 24 GiB | `/mnt/library` | `jellyseerr` / `qbit` / `sab` | running |
|
| 122 | [arriman](122-arriman.md) | 192.168.8.132 | priv | 4 | 8 GiB | 24 GiB | `/mnt/library` | `jellyseerr` / `qbit` / `sab` | running |
|
||||||
| 124 | [authentik](124-authentik.md) | 192.168.8.180 | priv | 2 | 4 GiB | 20 GiB | — | `auth.hubris.network` | running |
|
| 124 | [authentik](124-authentik.md) | 192.168.8.180 | priv | 2 | 4 GiB | 20 GiB | — | `auth.hubris.network` | running |
|
||||||
| 126 | [plato](126-plato.md) | 192.168.8.190 | priv | 2 | 2 GiB | 8 GiB | `/mnt/library/documents/plato` | `plato.hubris.network` | running |
|
| 126 | [plato](126-plato.md) | 192.168.8.190 | priv | 2 | 2 GiB | 8 GiB | `/mnt/library/documents/plato` | `plato.hubris.network` | running |
|
||||||
|
| 128 | [trmnl](128-trmnl.md) | 192.168.8.211 | unpriv | 1 | 768 MiB | 8 GiB | — | `trmnl.hubris.network` | running |
|
||||||
|
|
||||||
## Recently destroyed (kept for archaeology)
|
## Recently destroyed (kept for archaeology)
|
||||||
|
|
||||||
|
|||||||
@@ -42,6 +42,7 @@ LAN clients resolve via the [Technitium DNS on dns (107)](dns.md) → `192.168.8
|
|||||||
| ------------------------------ | -------------------------------- | -------------------------------- | -------------------------------------------- | ------------------------------------------ |
|
| ------------------------------ | -------------------------------- | -------------------------------- | -------------------------------------------- | ------------------------------------------ |
|
||||||
| `artifacto.hubris.network` | `/p/*`, `/static/*`, `/healthz` | `192.168.8.205:3100` | `artifacto-strip-sso` + `artifacto-ratelimit` (50 rps / 100 burst) | `fullchain.crt` / `privkey.key` |
|
| `artifacto.hubris.network` | `/p/*`, `/static/*`, `/healthz` | `192.168.8.205:3100` | `artifacto-strip-sso` + `artifacto-ratelimit` (50 rps / 100 burst) | `fullchain.crt` / `privkey.key` |
|
||||||
| `blog.hubris.network` | whole host | `192.168.8.205:8080` | `blog-ratelimit` (100 rps / 200 burst) | `blog.fullchain.crt` / `blog.privkey.key` |
|
| `blog.hubris.network` | whole host | `192.168.8.205:8080` | `blog-ratelimit` (100 rps / 200 burst) | `blog.fullchain.crt` / `blog.privkey.key` |
|
||||||
|
| `trmnl.hubris.network` | whole host | `192.168.8.211:9851` ([trmnl 128](../containers/128-trmnl.md)) | `trmnl-ratelimit` (20 rps / 40 burst) | `trmnl.fullchain.crt` / `trmnl.privkey.key` |
|
||||||
|
|
||||||
`artifacto-strip-sso` blanks inbound `X-Authentik-*` and `X-Artifacto-Gateway` so external clients can't spoof the SSO auto-login header contract. Path split is enforced at the VPS router rule, not by home Caddy. See [Artifacto on apps (105)](../containers/105-apps.md).
|
`artifacto-strip-sso` blanks inbound `X-Authentik-*` and `X-Artifacto-Gateway` so external clients can't spoof the SSO auto-login header contract. Path split is enforced at the VPS router rule, not by home Caddy. See [Artifacto on apps (105)](../containers/105-apps.md).
|
||||||
|
|
||||||
@@ -85,6 +86,9 @@ No cert-mirror entry and no `hubris-public-cert-sync.sh` mapping is needed for `
|
|||||||
|
|
||||||
## Changelog
|
## Changelog
|
||||||
|
|
||||||
|
### 2026-06-24 — `trmnl.hubris.network` exposed
|
||||||
|
TRMNL plugins middleware on [trmnl (128)](../containers/128-trmnl.md). File-provider router `trmnl-public` → `192.168.8.211:9851`, `trmnl-ratelimit` (20 rps / 40 burst), cert mirrored as `trmnl.fullchain.crt`/`trmnl.privkey.key`. Note: provisioned while the VPS↔home netbird route was down (`No networks available`, 3/6 peers — artifacto/blog 504 too); serves once the mesh route recovers.
|
||||||
|
|
||||||
### 2026-05-31 — `auth.hubris.network` now served locally on the VPS
|
### 2026-05-31 — `auth.hubris.network` now served locally on the VPS
|
||||||
Authentik migrated onto the VPS ([investigation](../investigations/2026-05-31-authentik-vps-migration.md)). Unlike the home-backed services above, `auth` is a local container routed via traefik Docker-provider labels with traefik-managed Let's Encrypt — no cert-mirror, no `traefik-dynamic.yaml` router. Admin UI gated by an ipAllowList middleware. Traefik gained a second Docker network (`auth`, `172.30.1.0/24`) to reach it while keeping its DB/Redis isolated from the netbird stack.
|
Authentik migrated onto the VPS ([investigation](../investigations/2026-05-31-authentik-vps-migration.md)). Unlike the home-backed services above, `auth` is a local container routed via traefik Docker-provider labels with traefik-managed Let's Encrypt — no cert-mirror, no `traefik-dynamic.yaml` router. Admin UI gated by an ipAllowList middleware. Traefik gained a second Docker network (`auth`, `172.30.1.0/24`) to reach it while keeping its DB/Redis isolated from the netbird stack.
|
||||||
|
|
||||||
|
|||||||
@@ -78,6 +78,10 @@ services:
|
|||||||
artifacto:
|
artifacto:
|
||||||
backend: apps
|
backend: apps
|
||||||
url: https://artifacto.hubris.network
|
url: https://artifacto.hubris.network
|
||||||
|
trmnl:
|
||||||
|
backend: trmnl
|
||||||
|
url: https://trmnl.hubris.network
|
||||||
|
note: self-hosted middleware for TRMNL e-ink plugins (polled by TRMNL cloud)
|
||||||
zimaos:
|
zimaos:
|
||||||
url: https://zimaos.hubris.network
|
url: https://zimaos.hubris.network
|
||||||
backend: zimaos
|
backend: zimaos
|
||||||
@@ -117,6 +121,15 @@ hosts:
|
|||||||
mounts:
|
mounts:
|
||||||
- /mnt/library
|
- /mnt/library
|
||||||
age_pubkey: age1xkklkvnk5z0fsnh6cfgv70hy9ksfy8rdprwerzw4yk3p4p7cxcqs2yvpz6
|
age_pubkey: age1xkklkvnk5z0fsnh6cfgv70hy9ksfy8rdprwerzw4yk3p4p7cxcqs2yvpz6
|
||||||
|
trmnl:
|
||||||
|
kind: lxc
|
||||||
|
pve_id: 128
|
||||||
|
host: hubris
|
||||||
|
os: linux
|
||||||
|
role: trmnl-middleware
|
||||||
|
lan_ip: 192.168.8.211
|
||||||
|
public_host: trmnl.hubris.network
|
||||||
|
# not yet mesh/SOPS-enrolled — see containers/128-trmnl.md
|
||||||
jellyfin:
|
jellyfin:
|
||||||
kind: lxc
|
kind: lxc
|
||||||
pve_id: 101
|
pve_id: 101
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ Pre-flight runbooks for planned changes that haven't happened yet. Once executed
|
|||||||
|
|
||||||
| Date | Title | Status |
|
| Date | Title | Status |
|
||||||
| ---- | ----- | ------ |
|
| ---- | ----- | ------ |
|
||||||
| 2026-06-24 | [TRMNL plugins LXC (128) + middleware deploy pipeline](2026-06-24-trmnl-plugins-lxc.md) | Planned |
|
| 2026-06-24 | [TRMNL plugins LXC (128) + middleware deploy pipeline](2026-06-24-trmnl-plugins-lxc.md) | In Progress |
|
||||||
| 2026-06-01 | [Slate AX → SODOLA managed switch migration](2026-06-01-slate-ax-to-sodola-migration.md) | Done |
|
| 2026-06-01 | [Slate AX → SODOLA managed switch migration](2026-06-01-slate-ax-to-sodola-migration.md) | Done |
|
||||||
|
|
||||||
## Conventions
|
## Conventions
|
||||||
|
|||||||
Reference in New Issue
Block a user