Nazeem.Me

A blog about technology, football and all the other random stuff in my life

Category: Homelab

The home network, the Proxmox cluster, storage, and everything I keep rebuilding.

  • I Built a Dashboard for the Things I Actually Check

    Five separate logins became one page. The interesting part was how often I turned out to be wrong.

    I check five things regularly: whether the homelab is healthy, my portfolio, my health trend, whatever trip is coming up, and my to-do list. Each one lived somewhere different. Each had its own login. None of them talked to each other. I have to go looking for information by opening a browser, going to my bookmarks page and finding the right tile to click:

    My Heimdall Dashboard of links to my self-hosted services.

    Information you have to go and fetch is information you check less often than you should.


    What It Looks Like Now

    One page. Five modules, each running in its own container, pulled together by a dashboard shell called Glance. The shell is configuration, not a product — it reads each service’s API and renders the numbers. That’s the whole trick (admittedly greatly helped by LLMs to design):

    The architecture for my data dashboard

    The health module was the biggest piece by some distance. I exported four years of Fitbit history and imported it alongside live Garmin data, so the trend line runs continuously from March 2022 to today instead of restarting when I changed watches. About 7 million data points. There’s a real gap of six days in January where neither device recorded anything, which is the week I switched. I’ve left it in. It’s honest.

    The finance module pulls positions from my broker and reconciles them against manually entered accounts for the things a broker doesn’t know about — Provident Fund, cash, the rest. Everything now sits behind a single login.


    Three of My Four API Guesses Were Wrong

    This is the part worth writing down.

    I planned the whole thing on paper first (with the help of AI, of course, including this post), including which endpoint each module would call and which fields it would read. When I came to build it, three of the four were wrong.

    The monitoring tool returned a flat structure where I’d assumed a nested one (I needed the LLM to explain this to me too!). The finance app had moved its performance endpoint to a new API version, and the old path returned a 404. The travel app used different field names to what I was expecting.

    None of these would have crashed anything. Each would have quietly rendered an error tile on a dashboard I’d then have trusted. The fix was boring and it worked: read a live response from every endpoint before writing a single line of template code.


    The Service I Decided Not to Build

    The last gap was authentication. Several services were reachable on my network with no login at all, and the obvious answer was to put single sign-on in front of everything.

    So I drafted the whole build. Container, secrets, rollout order, backups. Then I actually tested my assumptions and found that most of the services I’d listed as unprotected were, in fact, protected. The real list was less than half of what I’d written down (i.e. in my issue backlog in my Obsidian Vault). A lot of the services were things I wanted open so I could use them on the fly on an ipad or my phone.

    I closed the gap with tools already running instead. I stopped proxying my router’s admin interface entirely rather than putting a login in front of it, and turned off open registration on one app that had it. That took an afternoon and added nothing new to maintain.

    A new service is a permanent cost. Backups, updates, secrets to store, another thing that breaks at 2am. Since I already stayed up late last night to figure out why Jellyfin was transcoding when no one was using it, I figured “Simplex sigillum veri“.


    What I’d Tell Someone Starting

    Verify before you build. Every assumption I wrote in my .md vault was cheap to check and expensive to get wrong.

    Back up the databases properly. A snapshot of a running database is a snapshot of a database mid-write. Every module here dumps itself fifteen minutes before the nightly backup, and each dump refuses to complete unless it passes an integrity check. A backup you haven’t verified is a hope.

    And scope the problem before you scope the solution. I nearly built an identity provider to solve a problem that turned out to be four services and a router.

  • Two Brains for My AI: How Obsidian and Trilium Split the Work

    I've spent the last few months tinkering with a local AI stack on my desktop — Ollama for local inference, Hermes orchestrating the whole thing, and a Claude subscription for the heavy lifting. It works. But there was always one nagging gap I kept dancing around: where does the AI's memory actually live?

    For a while I went back and forth on whether to use Obsidian or Trilium as the knowledge store. I tried picking one. I tried picking the other. And then it finally clicked that I was asking the wrong question. They're not competitors. They do genuinely different jobs, and once I let them do those different jobs, the architecture got simpler, not more complicated.

    So here's where I landed.


    The Realisation: Two Kinds of Memory

    The thing nobody tells you about "AI memory" is that it's actually two separate problems wearing one coat:

    1. Reference knowledge — the curated, stable stuff. Things I've written down deliberately and want to keep. This is read-mostly. The AI pulls from it, but it shouldn't be scribbling all over it.
    2. Working memory — the daily log. Decisions, scratch notes, "remember I changed X today." This is high-frequency, messy, and constantly being written to by both me and the AI.

    The moment I framed it that way, the tool choice answered itself. I already use both apps for exactly these two purposes, so I'd been overthinking it.

    • Obsidian holds my .md files — my curated reference library. Plain markdown on disk, portable, mine forever.
    • Trilium is where I dump my daily notes. It's a proper database with an API, which turns out to matter a lot for the working-memory job.

    One is the library. The other is the journal. Don't make the library be the journal.


    Why The Split Plays To Each App's Strengths

    This isn't arbitrary. Each app is built for the role I'm giving it.

    Obsidian being plain markdown means anything can read it — Claude Code just cds into the folder, and an in-vault RAG plugin like Smart Connections does local semantic search through Ollama without phoning anywhere. Brilliant for retrieval. But writing back to markdown with an automated agent is fragile — no schema, no permissions, and with files syncing around my network, I'm one race condition away from a merge headache. So I don't let the AI write here. If it produces something worth keeping, I promote it by hand.

    Trilium is the opposite shape. It's a database behind an API (ETAPI), with native MCP support and proper permission control — read-only or read/write, my choice. That makes the agent-writes-back loop actually reliable. Dated entries, structured attributes, an interface Hermes and Claude can both hit cleanly. Exactly what you want for a journal the AI helps maintain.

    So: Obsidian gets read-only treatment because it's my canon. Trilium gets read/write because it's the working log and it's structurally built to handle it.


    The Architecture

    Here's the whole thing on one diagram:

       Obsidian Vault (.md)              Trilium (daily notes / DB)
       reference knowledge               working memory + journal
            │                                   │
            │ READ-mostly                       │ READ + WRITE
            ▼                                   ▼
       ┌─────────────┐                   ┌──────────────┐
       │ Smart Conn. │                   │ Built-in AI  │
       │ + Ollama    │                   │ + ETAPI/MCP  │
       │ Claude Code │                   │              │
       └──────┬──────┘                   └──────┬───────┘
              │                                 │
              └──────────► Hermes ◄─────────────┘
                      (orchestrator: routes
                       reads vs writes)
                              │
                      ┌───────┴───────┐
                      ▼               ▼
                   Ollama          Claude
                (local worker)   (escalation)

    The key shift here is that Hermes stops being just an orchestrator and becomes a router. It knows where each operation belongs. The rule set is almost embarrassingly simple:

    • Reads hit both stores — reference from Obsidian, recent context from Trilium.
    • Writes go to Trilium only, scoped to the daily-notes subtree.
    • Obsidian writes are human-gated. Always.

    Underneath both, the same two-tier model I've been running all along: Ollama does the routine local work (free, private, fast), and Claude gets the escalation — the complex reasoning and long-context synthesis that the local models can't touch.


    The Flow That Makes It Worth The Effort

    Running two apps only pays off because of how they feed each other:

    1. Capture. Daily stuff — decisions, meeting notes, half-formed ideas — gets written to Trilium. By me, or by the agent.
    2. Retrieve. When I ask a question, Hermes pulls reference context from Obsidian and recent working memory from Trilium, then stitches them into the prompt. Recent Trilium entries first (what's top of mind), then Obsidian reference matches (the background), then my actual question. Keeps the prompt focused and stops it bloating past the context window.
    3. Promote. Every so often, the good stuff from the Trilium daily log graduates into a proper Obsidian .md note. The journal is ephemeral; the library is forever. This step is manual on purpose — it's the quality gate that keeps my curated vault clean.

    That promotion step is genuinely the whole point. Trilium absorbs all the messy, high-frequency writes — which protects my Obsidian vault from being cluttered up by an over-eager agent — and only vetted, distilled knowledge ever crosses the line into the library.


    A Couple Of Guardrails

    Because I don't trust anything with write access until it's earned it:

    • Trilium's AI features are still experimental, so I started the MCP connection and Hermes both at read-only, and only promoted to read/write once I'd watched them behave for a while.
    • The agent writes into a dedicated subtree — an AI Memory parent note — rather than scattering machine-written content through my real notes. Keeps it quarantined, easy to audit, trivial to roll back if it goes sideways.

    Where This Leaves Me

    No separate vector database, no extra infrastructure to babysit. Obsidian's RAG handles reference retrieval, Trilium's ETAPI handles dated working memory, and each app does the one thing it's actually good at. Hermes routes between them, Ollama does the grunt work, Claude handles the hard problems.

    The lesson, as usual, was that I'd been overcomplicating it. The answer wasn't a clever tool or a new piece of kit — it was just letting two tools I already used keep doing their jobs, and being disciplined about which one the AI is allowed to write to.

    If you've made it this far: yes, I really did spend an unreasonable amount of time deciding where a chatbot is allowed to save its notes. Worth it though.

  • Home Network Topology 2026: The Full Map

    Between the 2024 network upgrade post and the Homelab 1.0 post, I’ve talked a lot about individual components — the 10G switches, the Proxmox cluster, the NAS stack. But I’ve never actually drawn the full picture of how everything connects. Partly because it was always evolving, and partly because I suspect I was slightly embarrassed about how complicated it had gotten.

    Well, here it is.

    Fair warning: it’s a lot.


    The Spine

    Everything in the house hangs off two devices: an Asus RT-BE88U running Merlin firmware as the main router, and a TP-Link TL-SX1008 8-port 10G switch that acts as the core distribution layer. The router connects to the switch via one of its 10G ports, and from the switch, a single 10G run goes to each room. This is the backbone that all four zones of the house branch off from.

    A quick correction from my 2024 post, where I said the house was running Cat 5E — that was wrong. It’s actually Cat 6 throughout. Apologies for the confusion. Cat 6 is rated for 10G up to 55 metres, so the 10G speeds across the house make complete sense, and I can stop sounding like I’m living dangerously.


    Media Console

    The media console area is the most “legacy” part of the network. It uses a 1G switch to service the TV, Xbox 360, Xbox One X, Nintendo Switch, Raspberry Pi 5 (running OpenElec), Denon AVR, and the HTPC. None of these devices need more than gigabit, so there’s no point in running 10G cabling to each of them. The 1G switch itself connects back to the main 10G switch, so aggregate throughput is fine.

    The one exception in this zone is the Proxmox test node, which gets a direct 10G connection from the main switch. I use this node to experiment with new configs before rolling anything out to the main cluster in the study room — having it on full-speed connectivity matters when I’m moving VM images around.


    Bedrooms

    Bedroom 1 is simple: a direct 10G connection straight to the gaming PC. No middlemen.

    Bedroom 2 has a second RT-BE88U running in AP mode, which provides Wi-Fi coverage to that end of the house. It also has the TV and Denon AVR wired into it directly. I previously ran a GT-AX11000 Pro in this role, but after the primary router upgrade, having two RT-BE88U units running Merlin in AiMesh mode has made the whole setup much more coherent.

    One thing I’m looking forward to exploring with the RT-BE88U is Guest Network Pro, which allows each wireless SSID to be assigned to its own VLAN. This means I could eventually segment the IoT and media devices in this zone — the TV, the Denon — onto their own isolated network, completely separate from the rest of the house. It’s the kind of proper network segmentation I’ve been putting off, and having hardware that natively supports it on both the primary router and the AP removes the main excuse I had for not doing it.


    Study Room: Where It Gets Complicated

    My homelab in a rack

    This is where most of my infrastructure lives, and the network reflects that.

    A TP-Link 5-port 10G switch sits at the top of the study room hierarchy, connected back to the main 10G switch. From there, things fan out:

    • DS920+ NAS and ZimaOS NAS each get a direct 10G connection from the 5-port switch. These are the primary working NAS devices, and they need fast access to the rest of the network.
    • An 8-port 10G switch provides 10G connections to the workstations: DS1621+ NAS, Unraid NAS, Windows 11 desktop, Mac Mini M4, and CachyOS desktop. Everything in this group transfers data at full 10G speeds, which makes a real difference when shuffling large files to and from the NAS.

    The interesting bit is the Proxmox cluster networking, which has two separate paths:

    The 8-port 2.5G switch handles the data network. Each of the three Proxmox nodes connects with two bonded 2.5G ports — so each node has an effective 5G data link for VM traffic and storage replication.

    The 8-port 1G switch handles the management network. Each Proxmox node has a dedicated 1G management interface, completely separate from the data path. The DS920+ gets two bonded 1G ports through this switch (for its management/backup traffic), and the ZimaOS NAS also has a 1G management link here. Keeping management traffic off the data network is something I should have done from the start — it makes the cluster much more stable and gives me a reliable out-of-band path when something inevitably breaks.


    What’s Next

    The topology is largely stable at this point, but there are a few things I’m considering:

    The 8-port 1G switch has a few spare ports that I’d eventually like to use for dedicated IPMI/iDRAC-style out-of-band management on the NAS devices — the Synology units support this to a degree, but I haven’t gotten around to configuring it properly.

    And with Cat 6 confirmed throughout the house, at least I can stop worrying about the cabling holding back my 10G links — that particular anxiety has been retired.

    If you’ve made it this far, congratulations — you now know more about my home network than most people probably want to. The diagram is linked above if you want to zoom in on any particular section.