… has too many hobbies.

Host Inventory for a (not-) Homelab

I don't really consider myself as running a proper "homelab." For starters, I own neither a proper network rack nor any rackable gear, and I see that as a prerequisite for calling your setup a "homelab." Nonetheless, I have a lot of physical and virtual machines to keep track of.

A thing I find useful sometimes is to be able to print a list of all my hosts. This ought to be simple enough, but what source of truth should I use? For hobby projects, any documentation is sure to fall out of date quickly.

The obvious solution, following a best practice that applies equally in programming or updating an electrical panel: documentation should be as near as possible to the implementation — unified, if possible.

For me, this means using my ~/.ssh/config as the source of truth. This file necessarily gets updated as I add or update machines, it supports comments, and it's checked into version control for sharing across all my machines. Perfect.

The following shell alias parses hosts out from ~/.ssh/config and print them to the terminal:

alias hosts-list='{ grep "^#\?Host " ~/.ssh/config | grep -v "Host \*" | sed "s/^Host //" | sed "s/^#Host /!/" ; }'

Output from the command looks like this:

burr burr.cdzombak.net #cloud #linux #deb
azul azul.cdzombak.net #cloud #linux #deb
wt wt.cdzombak.net #cloud #linux #deb
jetstream jetstream.tailnet-003a.ts.net #home #linux #deb
curie curie.tailnet-003a.ts.net #home #macos
opportunity opportunity.tailnet-003a.ts.net #home #macos
surroundings surroundings.tailnet-003a.ts.net #home #linux #deb #tiny #rpi
pidns pidns.tailnet-003a.ts.net #home #linux #deb #tiny #armbian
altdns altdns.tailnet-003a.ts.net #home #linux #deb #vm
pikvm pikvm.tailnet-003a.ts.net #home #linux #tiny #ro
jetkvm jetkvm.tailnet-003a.ts.net #home #linux #tiny #ro
pi400.dzhome #home #linux #deb #tiny #rpi
workshop-pi workshop-pi.tailnet-003a.ts.net #home #linux #deb #tiny #rpi #ro
mediactrpi mediactrpi.dzhome #home #linux #deb #tiny #rpi
rad2 rad2.dzhome #home #linux #deb #tiny #rpi #ro
labrackpwrmon labrackpwrmon.dzhome #home #linux #deb #tiny #rpi #ro
gamedeskpwrmon gamedeskpwrmon.dzhome #home #linux #deb #tiny #rpi #ro
mediactrpwrmon mediactrpwrmon.dzhome #home #linux #deb #tiny #rpi #ro
officebtns officebtns.dzhome #home #linux #deb #tiny #rpi
!planck (Windows laptop) #windows
!wilbur (Windows game desktop) #windows

There are a few special features here.

Tags: Many hosts have a sequence of tags after them that describe properties of the hosts (e.g. #deb means the system uses Debian-style package management). The relevant Host line in ~/.ssh/config looks like:

Host mediactrpwrmon mediactrpwrmon.dzhome #home #linux #deb #tiny #rpi #ro

This allows me to grep for just the hosts I'm interested in when performing a specific task. For example, if I want to apply a new Pi Reliability best practice to all my machines that use SD cards for storage, I can run hosts-list | grep '#tiny' for a list of the relevant machines.

Placeholders: Some hosts — most notably Windows machines — do not naturally show up in my SSH config. For these hosts, I've set a convention where I add an entry to !/.ssh/config like:

#Host wilbur (Windows game desktop) #windows

These entries are just comments as far as SSH is concerned, but hosts-list will parse them and include them in the list, prefixed by !.

Finally, I use a second alias that generates a Markdown-formatted "todo list" of hosts. This can be copied into e.g. Bear or processed into Things (using a Shortcut out of scope for this post) for project management/task tracking:

alias hosts-list-md='{ grep "^#\?Host " ~/.ssh/config | grep -v "Host \*" | sed "s/^Host /- [ ] /" | sed "s/^#Host /- [ ] !/" ; }'

Use and output of this command typically looks like this (in this example, finding all hosts that use a read-only root filesystem):

$ hosts-list-md | grep '#ro'
- [ ] pikvm pikvm.tailnet-003a.ts.net #home #linux #tiny #ro
- [ ] jetkvm jetkvm.tailnet-003a.ts.net #home #linux #tiny #ro
- [ ] workshop-pi workshop-pi.tailnet-003a.ts.net #home #linux #deb #tiny #rpi #ro
- [ ] rad2 rad2.dzhome #home #linux #deb #tiny #rpi #ro
- [ ] labrackpwrmon labrackpwrmon.dzhome #home #linux #deb #tiny #rpi #ro
- [ ] gamedeskpwrmon gamedeskpwrmon.dzhome #home #linux #deb #tiny #rpi #ro
- [ ] mediactrpwrmon mediactrpwrmon.dzhome #home #linux #deb #tiny #rpi #ro