Sunday, 31 May 2026

Batteries InClauded

Breaking into my homelab series to gather my thoughts on everyone's hot topic - LLM-driven coding aka AI Agents aka Vibe Coding. I've been doing this blog now for (gulp) 16 years so there's a pretty decent chance that at least some of my sentiments, and certainly some code snippets, have been slurped up and thrown into the giant soup pot full of [weights](https://maxleiter.com/blog/weights). I don't use any LLM assistance when writing posts, and I'm sure things are going to get extremely weird when future generations of LLMs have to deal with their own slop all over the web *as inputs*... It's absolutely been the case for a while that my work VSCode instance (with Copilot) will autocomplete a function given its TypeScript arguments and return type, basically character-for-character how I would have done it. "Coding" as the masses call it, has become a succession of [Tab] [Tab] [Tab] hits, followed by a careful inspection, reflection, then a shrug and a wry smile. __Very, very impressive autocomplete__ was the only way I'd been using LLMs up until *this month*. This month, *everyone* in my company got given unlimited Claude access. Yes, the CEO is one of those [AI maximalists](https://www.youtube.com/watch?v=DmU9uovmT2A). We also we given an "AI Hackday" to use said Claude access to do ... *anything* (as long as it somehow benefits the company). I decided (as did many others) to use Claude Code to build me a dashboard. But unlike most of the non-techies doing these things, I had *Opinions* about *how* Claude should do it. Namely, I wanted a server-side rendered SvelteKit app. Claude rolled up its sleeves and had Version 1.0 up-and-running in __7 minutes__. It worked flawlessly. We iterated on several additional features (mainly view filtering, state retention through URL parameters and rendering nested items). We had it feature-complete, bug-free and *precisely* what I'd been wanting in __just over 1.5 hours__. This included encountering 3 different classes of bugs - one that it spotted all by itself (a permissions problem in the `~/.npm` directory which meant it couldn't cache anything - it worked around it by using an explicit [writable] path for the cache in every `npm` command), one that it did not (it introduced a bug in client state that meant expanding and contracting container rows caused a full page reload), and one that we discovered "together" and fixed in a way that I can only describe as hyper-productive pair programming (it prepared three potential fixes, in parallel, and asked me to evaluate which one fixed the problem). All this would have taken me, starting from a bare-bones `npx sv create myapp` [blank slate](https://svelte.dev/docs/svelte/getting-started), 2-3 working days. I won't lie, I was extremely impressed. I still had an hour of coding time left though, so quit the Claude session and took a *really good look* at the code it had generated, in order to put a few finishing touches on. Styling and what-have you. __Ugh__. Firstly, I hadn't realised, but it had created an app based on __Svelte 4__ - a codebase that [hasn't been touched in a year](https://github.com/sveltejs/svelte/releases/tag/svelte%404.2.20). Clearly (and a general web search will confirm this) the majority of Svelte content out there is based on Svelte <5, and when I asked for a Svelte[Kit] app it went with what it "knew" best. I, on the other hand *do not know* Svelte 4, I picked it up at v5 specifically because I'd heard it was so much nicer to use (and it is - it's been my preferred web dev framework now for a couple of years). Secondly, the code was room-full-of-monkeys-writing-Shakespeare messy. Heaps of things being recalculated all over the place instead of done once in a nice function. CSS that "worked" but would break at the slightest change (which takes real effort in a Svelte app, I can tell you). Thirdly, the function that it wrote (hey, at least it encapsulated *something*) to work out how to find and nest (potentially infinitely deep) child items was, well, I think `O(n^3)` *might* cover the *computational* complexity, but in terms of human intelligibility it was __bonkers__. So it did the job __fast__, __cheap__ (in terms of how much it cost in tokens compared to the time it would have taken me at my hourly rate) and __good-enough__, as long as you didn't need to change it *in any way at all in the future*. I guess in a future Claude Code session I could get it to generate a battery of unit tests for the child-nesting function and then refactor it myself while ensuring all the tests pass. Whether anyone *cares* enough about __good-eough__ remains to be seen.

Sunday, 26 April 2026

Home-lab design (2026 edition) part 3 - Inside the Docker VM

This is the next part of my progressively-deeper look at my "home lab" setup in 2026 - now we're peering into the Docker VM itself. There are a million "here are the 19 Docker apps you ABSOLUTELY MUST run in 2026" listicles out there, that's fine. I don't run that many, and many of them are of a highly-personalised, only-on-my-network-thankyou nature that I'm not even going to mention. The key two (yes, just two) are [Nginx Proxy Manager](https://nginxproxymanager.com/) and [Arcane](https://getarcane.app/). The first because it means I can forget about individual internal network addresses, port numbers, and "this website is not secure" non-HTTPS browser warnings, the latter because it's a dead simple dashboard to see what's going on at the Docker level. I'm not even going to talk any more about Arcane because it's just so simple; it's exactly what you'd expect a Docker web UI to look like:
## Nginx Proxy Manager ALL THE THINGS NPM is just a really nice and simple frontend for an Nginx instance, and I must admit I may have got a little carried away re-front-ending almost every local-network-based web app I regularly use, viz: - Arcane - HAOS - My Synology NAS - My Mikrotik router - Octoprint - My Raspberry Pi - The Proxmox UI - My DLNA streamer, Serviio - My ISP router (a TP-Link) - All my personal scratch-my-own-itch apps; and - Nginx Proxy Manager itself(!) ### Setting up Now *most* of these webapps and Dockerized apps work *great* with Nginx Proxy Manager providing a wildcard HTTPS certificate and proxying for them. If it's a simple app like `http://nas:5000` it's the work of seconds to give it a better address. For a new Dockerized webapp, my process is: - Get it going and find out what port it's on, i.e. `webport` - Check I can hit it at `http://docker:webport` - Go into NPM and add a proxy host for `coolapp.lab.themillhousegroup.com` that points to `http://docker:webport`, and add force SSL on the SSL tab using my `*.lab.themillhousegroup.com` wildcard certificate - I've already got a corresponding "wildcard" DNS `CNAME` entry in the Mikrotik router that ensures `*.lab.themillhousegroup.com` will resolve to `docker`:
NPM automagically manages this certificate for me thanks to the wonders of the `DnsMulti` DNS-based certificate management system, which does the whole Lets Encrypt automated-renewal dance via any one of a [huge number of DNS providers](https://go-acme.github.io/lego/dns/#dns-providers) (I use Netlify which is as simple as configuring a Personal Use Token and giving it to the `dnsmulti` plugin as an environment variable) There are a couple of "difficult" apps that needed a little more configuration though. ### Home Assistant Doesn't take kindly to being proxied by NPM and has to have a couple of (very simple) stanzas added to its `configuration.yaml`: ``` # Allow NGinx Proxy Manager to proxy for homeassistant: # https://community.home-assistant.io/t/using-nginx-proxy-manager-with-homeassistant-all-via-docker/725019/6 # https://www.reddit.com/r/homeassistant/comments/1dp4nwf/hass_behind_nginx_proxy_manager/ http: use_x_forwarded_for: true trusted_proxies: - 10.240.0.105 # IP of docker - 10.240.0.0/24 # the Docker subnet - 127.0.0.1 # Add the localhost IPv4 address - ::1 # Add the localhost IPv6 address # ... and further to that: # https://community.home-assistant.io/t/using-nginx-proxy-manager-with-homeassistant-all-via-docker/725019/13 homeassistant: external_url: "https://haos.lab.themillhousegroup.com" ``` As you can probably tell, I like pasting the relevant web link into configuration docs; those links are to various fora - [Reddit `/r/homeassistant`](https://www.reddit.com/r/homeassistant/comments/1dp4nwf/hass_behind_nginx_proxy_manager/) for the `trusted_proxies` IP addresses and the [Home Assistant Community](https://community.home-assistant.io/t/using-nginx-proxy-manager-with-homeassistant-all-via-docker/725019/13) for the `external_url` setting. ### Proxmox Proxmox *really* wants you to use HTTPS everywhere, so you need to turn on ALL the SSL options in NPM, and ALSO use https *in the address of the proxied instance as well* - which is unusual, but makes sense, as it does create a self-signed certificate when you first set up your system. I basically followed [this guide on Reddit](https://www.reddit.com/r/Proxmox/comments/1jsgto6/accessing_proxmox_via_nginx_proxy_manager/) so my NPM setup for `proxmox.lab.themillhousegroup.com` looks like this (note the `https` in the Scheme - different to every other host!):

Saturday, 28 March 2026

Home-lab design (2026 edition) part 2 - Inside the NUC

Following on from last-month's look at the [physical boxes that make up my home "lab"](https://blog.themillhousegroup.com/2026/02/home-lab-design-pt1-physical-boxes.html) in 2026, there's only one box that actually deserves any deeper inspection; the Intel NUC5PGYH, an incredibly-lucky hard-rubbish find from a few years back that, after sourcing a suitable 19V (i.e. laptop) power supply, de-Windows-ing, and most recently, a RAM doubling (at the princely cost of $25) to 8Gb, has proven itself to be an ideal platform for various x86-based experimentation.
Up until last month, the NUC has been running Debian Bookworm, keeping up my long-standing tradition of using Debian-based Linux for my home servers. The primary applications running on this box before 2026 were [_Home Assistant_](https://www.home-assistant.io/) via the [packaged-up `haos.qcow2` virtual machine image](https://www.home-assistant.io/installation/linux/#install-home-assistant-operating-system), the excellent [_OctoPrint_](https://octoprint.org/) for [3D printer](https://blog.themillhousegroup.com/search/label/3dprinting) monitoring, the [_BrickSync_](https://bricksync.net/) inventory-management software that keeps multiple [online LEGO parts stores](https://blog.themillhousegroup.com/2024/06/home-cooked-and-delicious.html) in sync, and various self-written node.js-based webapps that aren't suitable for public hosting on the otherwise-brilliant (and free) [Netlify](https://www.netlify.com/) platform. HAOS runs as a VM, which was fairly easy to get going following the instructions as linked above, and pretty-much looks after itself. OctoPrint, being a "proper" solution, runs properly as a system service; the latter apps I'd run fairly erratically using `screen` sessions. Finally, after a particularly annoying festival of `screen`-cursing and port-interfering, I'd had enough. The SvelteKit apps I prefer these days are [fairly trivial to Dockerise](https://khromov.se/dockerizing-your-sveltekit-applications-a-practical-guide/), and I've spent enough time with Docker in my work life to appreciate the true segregation and repeatability that it brings - "cattle, not pets". And thus so began a new phase - transforming the NUC into a more-appropriate host for these "workloads". ### Moving it all over After carefully tidying up the home directory of the Debian installation (which featured the usual assortment of scripts, Github clones and random downloaded files), it was time to say goodbye. My new OS for the NUC is [Proxmox VE](https://www.proxmox.com/en/products/proxmox-virtual-environment/overview), which is still Debian-based underneath, so I guess my run continues. I liked the idea of completely installing from scratch, and having the absolute thinnest layer possible between the VM workloads and the bare metal. It certainly shows in Proxmox's boot time, which is extremely impressive; no starting desktops or initializing sound systems here - just straight down to business! The conceptual layout of the VMs inside Proxmox is also refreshingly simple: ```mermaid architecture-beta group prox(affinity:server)[Proxmox VE] service ahaos(affcircle:c-house-blue)[HAOS 100] in prox service zdocker(affcircle:c-nas-blue)[Docker 101] in prox ``` That's it. _Everything_ that's not Home Assistant will be run as a Docker container inside VM 101. Moving the `HAOS` image (that I'd backed up over to my NAS, simply by stopping the VM and copying the `haos.qcow2` file) was easy, courtesy of this excellent [step-by-step guide](https://forum.proxmox.com/threads/guide-install-home-assistant-os-in-a-vm.143251/). With `haos.qcow2` safely copied into your Proxmox `local` storage, you can install it like this in the main Shell: ``` root@nuc:/home# qm importdisk 100 /var/lib/vz/import/haos.qcow2 local-lvm importing disk '/var/lib/vz/import/haos.qcow2' to VM 100 ... Logical volume "vm-100-disk-1" created. Logical volume pve/vm-100-disk-1 changed. transferred 0.0 B of 32.0 GiB (0.00%) transferred 327.7 MiB of 32.0 GiB (1.00%) transferred 655.4 MiB of 32.0 GiB (2.00%) ... ```
Now for *Docker*, there are many opinions about the best way to run it, but I like an easy life, so another VM is my choice. I already had a `docker.qcow2` image, which runs a super-slim _Alpine Linux_ host OS, [just enough to host Docker](https://wiki.alpinelinux.org/wiki/Docker), so we do it all again. Exactly the same. Cattle, not pets ;-) ``` root@nuc:~# qm importdisk 101 /var/lib/vz/import/docker.qcow2 local-lvm importing disk '/var/lib/vz/import/docker.qcow2' to VM 101 ... Logical volume "vm-101-disk-1" created. Logical volume pve/vm-101-disk-1 changed. transferred 0.0 B of 32.0 GiB (0.00%) transferred 331.0 MiB of 32.0 GiB (1.01%) transferred 665.2 MiB of 32.0 GiB (2.03%) transferred 996.1 MiB of 32.0 GiB (3.04%) transferred 1.3 GiB of 32.0 GiB (4.06%) ``` ### Final touches (USB device mapping) I have a couple of USB devices that hang off the NUC, and Proxmox VE has actually substantially improved my quality-of-life with its device-ID-specific mapping which means even if a device is power-cycled (as happens all the time with the 3D printer; it's too noisy to leave on) it still remains consistently available to the consuming applications. Under Debian, such a device would appear as a new `bus:device` pair every time, causing havoc. #### Mapping the Sonoff Zigbee dongle through to the `haos` VM:
#### Mapping the 3D printer through to the `docker` VM:

Saturday, 21 February 2026

Home-lab design (2026 edition) part 1 - Physical Boxes

Any long-time readers of this blog will probably be sick of my Mermaid [network diagrams](https://blog.themillhousegroup.com/2025/07/mikrotik-automation.html), but hey, these posts are as much for me to document my own trials and tribulations as they are for you, so ... let's do another one! ```mermaid architecture-beta service internet(affinity:cloud) group home(affinity:house)[Home] service ispr(affcircle:c-firewall-green) [ISP Router] in home service gbe(affcircle:c-router-green)[Mikrotik] in home service pi(affcircle:c-server-blue)[Pi] in home service nuc(affcircle:c-server-blue)[NUC] in home service nas(affcircle:c-nas-blue)[NAS] in home internet:B --> T:ispr ispr:B --> T:gbe gbe:B -- T:pi gbe:L -- R:nas gbe:R -- L:nuc ``` Dead simple. The __NAS__, a __Synology DS209__, has been faithfully just doing its thing for 15(!) years now and aside from occasional, painless, disk size increments, just keeps keeping on. And after last year's introduction of the [__Mikrotik RouterBoard RB2011__](https://blog.themillhousegroup.com/2025/01/upgrading-to-mikrotik-routerboard.html) router for [DHCP](https://blog.themillhousegroup.com/2025/03/upgrading-to-mikrotik-routerboard.html) and [DNS](https://blog.themillhousegroup.com/2025/04/upgrading-to-mikrotik-routerboard.html), the __Raspberry Pi 4B__ has been freed up for more Pi-suitable tasks (light web serving, streaming, home automation tasks). Which leaves the __Intel NUC__, specifically a `NUC5PGYH` - a lucky roadside hard-waste find that has proven invaluable as a super-compact, super-efficient x86 server. This is the core of my "self-hosting" plan for 2026.

Saturday, 3 January 2026

2026 - Off we go again

Happy New Year! After some refreshing time off I'm looking to refresh my "home lab" with some self-hosted projects, so this is likely to be a big focus of this year's posts. Of course, as much as possible I'm still [doing](https://blog.themillhousegroup.com/2023/09/frenzyjs-saga-of-flood-fill.html) [projects](https://blog.themillhousegroup.com/2022/06/introducing-cardle.html) "out in the open" and hosting them on [Netlify](https://www.netlify.com), whose [SvelteKit](https://svelte.dev/docs/kit/introduction) [integration](https://svelte.dev/docs/kit/adapter-netlify) is still the smoothest and easiest way to get a modern, public-facing website that I've found. But for situations where full public access isn't appropriate, I've been experimenting with [Dockerization of Svelte apps](https://khromov.se/dockerizing-your-sveltekit-applications-a-practical-guide/) and self-hosted Docker and I'm liking what I'm seeing. More to come.