# Homelab inventory — canonical structured topology. # # Single source of truth. hosts/*.yaml is generated from this file by # mcp/build_host_files.py; do NOT edit those by hand. # # Conventions: # - hostname keys MUST match the actual `hostname` of the machine (on # macOS: `scutil --get LocalHostName` if set). # - `os:` one of: linux, macos # - `kind:` one of: proxmox-host, lxc, vm, workstation, external # ("external" is reserved for hosts the homelab CLI manages via ssh but # that aren't homelab clients themselves — e.g. the IONOS netbird VPS # with no /etc/age/key.txt and no /opt/homelab-context clone.) # - `mesh:` lists addresses the host is reachable at. Both `netbird` and # `tailscale` are accepted during the migration (see infrastructure/mesh.md). # Prefer netbird FQDNs over raw IPs. # - `age_pubkey:` provisioned by secrets-issuance on first bootstrap and # committed back via `homelab client add --finalize-pubkey `. # - When a service moves hosts, update only the `services:` section here; # never duplicate addresses elsewhere. # - `ssh.user:` per-host login user. Default is `root` if omitted (matches # every LXC + the PVE host). Set explicitly for workstations whose login # user differs from `root`. Used by the `homelab` CLI to build # `user@host` and to inform anyone running raw `netbird ssh` (which # defaults to the LOCAL username — the gotcha that creates "user not # found" errors when ssh'ing INTO machines that only have `root`). # # `homelab client add/remove` does surgical line-edits — comments survive. # Avoid round-tripping the file through yaml.safe_dump (it strips comments). mesh: primary: netbird accepted: - netbird - tailscale netbird_subnet: 100.122.0.0/16 netbird_domain: netbird.selfhosted # Service contract (Oikos, 2026-07-05): each service should carry # backend host/container that runs it (required) # url public URL if ingress-exposed # doc_page owning wiki page # config_repo tracked config repo, if any (mutations go commit+push) # health health-check URL if it differs from `url` # risk_notes what an agent must know before touching it # See oikos/ontology.yaml + oikos/policy.yaml. services: proxmox_ui: url: https://proxmox.hubris.network backend: hubris port: 8006 doc_page: hosts/hubris.md risk_notes: hypervisor UI — changes here affect every guest on the node gitea: url: https://git.hubris.network backend: gitea backend_url: http://192.168.8.121:3000 doc_page: containers/104-gitea.md config_repo: dtoro/gitea-customizations risk_notes: hosts all config repos + deploy webhooks; outage blocks auto-deploy and sync caddy: backend: caddy role: reverse-proxy note: terminates all *.hubris.network doc_page: containers/121-caddy.md config_repo: dtoro/caddy-conf risk_notes: wide blast radius — every *.hubris.network route rides on it (see oikos/policy.yaml service_overrides) authentik: url: https://auth.hubris.network backend: netbird-vps doc_page: containers/106-auth-outpost.md note: >- core runs on the VPS since 2026-05-31; LAN forward-auth outpost is auth-outpost (LXC 106) at 192.168.8.6:9000. Previous backend value "authentik" referenced the retired embedded-outpost host (LXC 124). risk_notes: SSO provider — outage locks login to OIDC/forward-auth services dns: backend: dns note: Technitium DNS, split-horizon zone doc_page: containers/107-dns.md risk_notes: LAN-wide resolver — misconfig breaks name resolution for every client jellyfin: url: https://media.hubris.network backend: jellyfin doc_page: containers/101-jellyfin.md risk_notes: native Authentik OIDC via SSO-Auth plugin, no Caddy forward-auth gate; VAAPI transcode depends on GPU passthrough on strong nextcloud: url: https://cloud.hubris.network backend: nextcloud doc_page: containers/114-nextcloud.md paperless: url: https://paperless.hubris.network backend: paperless doc_page: containers/103-paperless.md risk_notes: document archive — treat data as irreplaceable; DB operations are destructive-class matrix: url: https://matrix.hubris.network backend: elementsynapse doc_page: containers/118-elementsynapse.md risk_notes: alert/approval channel for Oikos — outage silences agent escalation photos: url: https://photos.hubris.network backend: mule-images doc_page: containers/120-mule-images.md config_repo: dtoro/mule-image arr_stack: backend: arriman note: jellyseerr / qbit / sab on docker compose doc_page: containers/122-arriman.md artifacto: backend: apps url: https://artifacto.hubris.network doc_page: containers/105-apps.md config_repo: dtoro/Artifacto trmnl: backend: trmnl url: https://trmnl.hubris.network note: self-hosted middleware for TRMNL e-ink plugins (polled by TRMNL cloud) doc_page: containers/128-trmnl.md config_repo: dtoro/terminalito zimaos: url: https://zimaos.hubris.network backend: zimaos doc_page: vms/100-zimaos.md haos: backend: haos doc_page: vms/108-haos.md homelab_mcp: backend: apps port: 9810 systemd_unit: homelab-mcp public_host: mcp.hubris.network endpoint: https://mcp.hubris.network/mcp doc_page: infrastructure/homelab-context.md config_repo: dtoro/Homelab-Docs note: MCP server. Read-only context + management. Reachable on the LAN via Caddy and from off-LAN via Netbird (192.168.8.0/24 is a network resource routed through hubris). risk_notes: agents' primary read surface — outage degrades every agent to grepping the clone secrets_issuance: backend: apps port: 9820 systemd_unit: secrets-issuance public_host: secrets.hubris.network endpoint: https://secrets.hubris.network/issue doc_page: operations/agent-enrollment.md config_repo: dtoro/Homelab-Docs note: Issues per-client age private keys. Gated at source-IP layer (mesh + LAN subnets in MESH_SUBNETS). risk_notes: identity issuance — any change is security-sensitive; key operations are destructive-class hosts: hubris: kind: proxmox-host os: linux role: hypervisor lan_ip: 192.168.8.77 mesh: netbird: ip: 100.122.38.109 fqdn: proxmox-server.netbird.selfhosted ssh: port: 22 netbird_port: 22022 user: root mounts: - /mnt/library 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 house: kind: lxc pve_id: 129 host: strong os: linux role: family-planner lan_ip: 192.168.8.244 public_host: house.hubris.network notes: - Docker host for Yuvomi (family planner). Created 2026-06-26. - Migrated from hubris to strong 2026-07-05 (Phase 1 of strong migration plan). - Runs Yuvomi container + WebDAV doc bridge to paperless - 192.168.8.212 was the hubris IP before migration (now used by teddycloud via DHCP) age_pubkey: age1s07zs83ehtlg8jtwvr75ltc3c4cdlemfwjuxrwjtwkqxkl9tpggsyrzn2h jellyfin: kind: lxc pve_id: 101 host: strong os: linux role: media-server lan_ip: 192.168.8.246 public_host: media.hubris.network mesh: tailscale: fqdn: jellyfin mounts: - /mnt/media_local notes: - Jellyfin 10.11.11 with VAAPI hardware acceleration (Radeon 680M iGPU on strong) - 4 cores / 8 GiB RAM / 1 GiB swap - SSO-Auth plugin v4.0.0.4 with Authentik OIDC (no Caddy forward-auth gate) - GPU passed via dev0+dev1: /dev/dri/renderD128 + card0 - Migrated from hubris to strong 2026-07-05 (Phase 2). Library on ludo-lvm. age_pubkey: '' nfs-export: kind: lxc pve_id: 102 host: hubris os: linux role: storage-export lan_ip: 192.168.8.200 paperless: kind: lxc pve_id: 103 host: hubris os: linux role: document-archive lan_ip: 192.168.8.130 public_host: paperless.hubris.network mesh: tailscale: fqdn: paperless mounts: - /mnt/library age_pubkey: '' gitea: kind: lxc pve_id: 104 host: hubris os: linux role: git-server lan_ip: 192.168.8.121 public_host: git.hubris.network backend_port: 3000 mesh: tailscale: fqdn: gitea mounts: - /mnt/library notes: - Bare repos live at /mnt/library/repos/dtoro/*.git age_pubkey: '' apps: kind: lxc pve_id: 105 host: hubris os: linux role: docker-apps lan_ip: 192.168.8.205 public_hosts: - artifacto.hubris.network mesh: tailscale: ip: 100.121.171.122 fqdn: apps mounts: - /mnt/library runs: - artifacto - plantuml - homelab-mcp - secrets-issuance # booklore removed 2026-06-29 → migrated to grimmory (LXC 130) age_pubkey: age1duyl8mkpgu80uv934dy8q7enqjms6yvdz264hme8uryuxmvvqesq6rusq0 auth-outpost: kind: lxc pve_id: 106 host: hubris os: linux role: authentik-gateway lan_ip: 192.168.8.6 notes: - Runs Authentik outpost (reverse-proxy/SSO enforcement) for protected services dns: kind: lxc pve_id: 107 host: hubris os: linux role: dns-server lan_ip: 192.168.8.2 notes: - Technitium DNS, split-horizon zone for *.hubris.network - Primary DNS for 192.168.8.0/24 LAN (inventory.services.dns references this) nextcloud: kind: lxc pve_id: 114 host: hubris os: linux role: file-sync lan_ip: 192.168.8.224 public_host: cloud.hubris.network mesh: tailscale: fqdn: nextcloud mounts: - /mnt/library age_pubkey: '' elementsynapse: kind: lxc pve_id: 118 host: strong os: linux role: matrix-server lan_ip: 192.168.8.242 public_host: matrix.hubris.network mesh: tailscale: {} notes: - Migrated from hubris to strong 2026-07-05 (Phase 1 of strong migration plan). sophia: kind: lxc pve_id: 119 host: hubris os: linux role: workshop lan_ip: 192.168.8.109 mesh: tailscale: fqdn: sophia mounts: - /mnt/library age_pubkey: '' mule-images: kind: lxc pve_id: 120 host: hubris os: linux role: photo-management lan_ip: 192.168.8.136 public_host: photos.hubris.network mesh: tailscale: fqdn: muleimage mounts: - /mnt/library age_pubkey: '' caddy: kind: lxc pve_id: 121 host: hubris os: linux role: reverse-proxy lan_ip: 192.168.8.175 notes: - Terminates all *.hubris.network - /etc/caddy is a git checkout of dtoro/caddy-conf peers: - authentik - gitea arriman: kind: lxc pve_id: 122 host: strong os: linux role: arr-stack lan_ip: 192.168.8.245 public_hosts: - jellyseerr.hubris.network - qbit.hubris.network - sab.hubris.network mesh: tailscale: fqdn: arr mounts: - /mnt/media_local notes: - Migrated from hubris to strong 2026-07-05 (Phase 2). Library on ludo-lvm. - Contains homarr, radarr, sonarr, lidarr, sabnzbd, qbittorrent, bazarr, flaresolverr, prowlarr, jellyseerr - qBittorrent auth subnet whitelist expanded to 192.168.8.0/24 (for seanime + Caddy access) age_pubkey: '' grimmory: kind: lxc pve_id: 130 host: strong os: linux role: book-library lan_ip: 192.168.8.247 public_host: books.hubris.network mounts: - /mnt/media_local notes: - Docker host for Grimmory (community fork of Booklore). Created 2026-06-29. - Migrated from hubris to strong 2026-07-05 (Phase 2d). Books on ludo-lvm. age_pubkey: age1uellsemnjrzgfg9fxw4jefpy05laxzggwnwhh6ny3wl7alyp6v8q0muxet seanime: kind: lxc pve_id: 133 host: strong os: linux role: anime-media-server lan_ip: 192.168.8.248 public_host: seanime.hubris.network mounts: - /mnt/media_local/anime notes: - Seanime anime media server for online streaming + local library scanning - Created 2026-07-05. Binary at /opt/seanime/bin/seanime, systemd service. - Connected to qBittorrent on arriman (192.168.8.245:8080) - 8 online streaming extensions installed (HiAnime, AniWatch, KickAssAnime, etc.) - /anime mounted from strong ludo-lvm (/mnt/media_local/anime) - Caddy: https://seanime.hubris.network → 192.168.8.248:43211 - qBittorrent auth subnet whitelist expanded to 192.168.8.0/24 for seanime access romm: kind: lxc pve_id: 134 host: strong os: linux role: rom-manager lan_ip: 192.168.8.249 public_host: roms.hubris.network mounts: - /mnt/media_local notes: - Docker host for RomM (romm.app) self-hosted ROM manager. Created 2026-07-05. - MariaDB sidecar at /opt/romm/docker-compose.yml. - ROMs on ludo-lvm media volume at /mnt/media_local/roms. - 1 core / 2 GiB RAM / 16 GiB rootfs (ludo-lvm). zimaos: kind: vm pve_id: 100 host: hubris os: linux role: nas-frontend-eval lan_ip: 192.168.8.195 public_host: zimaos.hubris.network haos: kind: vm pve_id: 108 host: hubris os: linux role: home-automation lan_ip: 192.168.8.101 mesh: tailscale: fqdn: homeassistant republic-laptop: kind: workstation os: linux role: primary-dev mesh: netbird: fqdn: republic-laptop.netbird.selfhosted ssh: user: dtoro mac-mini: kind: workstation os: macos role: dev lan_ip: 192.168.178.182 mesh: netbird: fqdn: mac-mini-234-17.netbird.selfhosted ssh: user: dtoro notes: - Only macOS in the fleet. Bootstrap uses launchd. age_pubkey: age1z62ff2ak9zj5ctcvaxwyyhedwjvlwgm2dkn9nk3wrwk8fkavcpmsqwc2vs strong: kind: proxmox-host os: linux role: hypervisor lan_ip: 192.168.178.181 ssh: user: root notes: - Reformatted from Linux workstation ("ludo-mini" in this wiki, still the machine's nickname) to Proxmox VE 9.2.3 on 2026-07-01. Renamed the inventory/wiki identity from ludo-mini to strong on the same day so it matches the OS/cluster hostname everywhere (bootstrap looks up hosts/$(hostname).yaml, so a mismatch would break enrollment). - Joined hubris's "Homelab" cluster same day. 2-node, no QDevice tiebreaker yet — see hosts/hubris.md quorum note. - Netbird not yet installed (fresh OS wiped prior enrollment); reachable today only via the household LAN / existing Fritz static route to 192.168.8.0/24. Re-enroll in mesh as a follow-up if off-LAN access to this host itself (not just its future guests) is needed. - First step of the planned library-SSD migration — see .hermes/plans/2026-06-03_110000-library-ssd-migration-to-ludo-mini.md (filename kept as-is, it's a historical planning doc). Only Phase 1 (Proxmox install + cluster join) is done; no physical drive move, service migration, or GPU passthrough has happened yet. age_pubkey: age1rtwvdct6avjkr3cyxv3vue3vqx4d524fjfr3vk7xrnvyrylnry5sm54sn4 netbird-vps: kind: external os: linux role: netbird-mgmt mesh: netbird: ip: 100.122.165.149 fqdn: netbird-ionos.netbird.selfhosted ssh: user: root notes: - Public IONOS VPS — hosts the vanilla netbird mgmt+signal+relay+dashboard stack + host coturn (see infrastructure/vps-hardening.md + infrastructure/mesh.md changelog 2026-05-21). - NOT a homelab client. No /etc/age/key.txt, no /opt/homelab-context clone. Managed via ssh from hubris; sshd is locked to hubris's pubkey. - Public IPv4 82.165.190.79. Auto-patching via unattended-upgrades. - Configs rendered by `homelab render-vps-configs` from vps/turnserver.conf.tmpl + vps/management.json.tmpl, with secrets decrypted from secrets/turn-shared-secret.yaml + secrets/netbird-authentik-oidc.yaml on hubris. rclone: kind: lxc os: linux role: backup mesh: netbird: fqdn: rclone.netbird.selfhosted age_pubkey: age1pwtdws2thdh7vzp2dzttl3zxgcs2tgpcsjsqgw3q04nyml4kvuqq467u4x # Destroyed nodes (lifecycle state: destroyed — see oikos/ontology.yaml). # Kept so agents can answer "what happened to X?" from structured data and # so drift detectors can flag anything still referencing them. # Full narrative table: containers/index.md "Recently destroyed". archaeology: claudio-bot: kind: lxc pve_id: 123 destroyed: 2026-06-04 reason: replaced by Hermes Agent on mac-mini; monitoring moved to homelab-health-watchdog cron plato: kind: lxc pve_id: 126 destroyed: 2026-06-28 reason: notes workspace decommissioned; data retained at /mnt/library/documents/plato mule-photos-new: kind: lxc pve_id: 127 destroyed: 2026-05-22 reason: PhotoPrism test stack promoted to LXC 120 (Mulimage 2.0 merge) heaper: kind: lxc pve_id: 116 destroyed: 2026-05-14 reason: decommissioned; data retained at /mnt/library/heaper syncthing: kind: lxc pve_id: 109 destroyed: 2026-05-14 reason: decommissioned; library subtree was empty seafile: kind: lxc pve_id: 125 destroyed: 2026-05-13 reason: Seafile Pro evaluation rejected; files.hubris.network removed from caddy + dns arr-yunohost: kind: lxc pve_id: 100 destroyed: 2026-04-28 reason: migrated to docker stack on arriman (LXC 122) flaresolverr: kind: lxc pve_id: 106 destroyed: 2026-04-28 reason: folded into the arriman docker compose marimo: kind: lxc pve_id: 107 destroyed: 2026-04-28 reason: decommissioned photoprism: kind: lxc pve_id: 110 destroyed: 2026-04-28 reason: replaced by mule-images (LXC 120) karakeep: kind: lxc pve_id: 111 destroyed: 2026-04-28 reason: decommissioned immich: kind: lxc pve_id: 112 destroyed: 2026-04-28 reason: replaced by mule-images (LXC 120) reticulum: kind: lxc pve_id: 115 destroyed: 2026-04-28 reason: decommissioned