ToDo¶
-
Apps/Services
- NextCloud
- (wip)
*arrsuite (Radarr,Sonarr, Readarr, Lidarr, Bazaar,Prowlarr, Profilarr)- rreading-glasses as metadata service
- Media streaming via Jellyfin / Jellyseer (shared with fam)
- Vaultwarden
- Mealie
- Ebook/Audiobook manager via Kavita + Audiobookshelf
- Also evaluate Calibre vs Kavita
- Actual for budget management
- StirlingPDF for PDF tools
- Syncthing to sync some files between devices
- Extremely useful if you own a PocketBook e-reader, which runs basically linux, so you can run Syncthing on it, meaning you... get... to... sync... your... eBooks... directly! 🤯😲😱
- Whatever else I have starred in 'Self-Hosted' category on github that might be useful for home-use or to play around with
- ... TBC
-
Improve docs and add configuration script for new users
- Ansible inventory config for metal provisioning
- Prompt user to edit the inventory file
- Update docs - add sample inventory file contents for reference
- Search-and-replace throughut the repo files:
- Domain name
- Github repository name
- ... ?
- Ansible inventory config for metal provisioning
-
Automate personal gitea user provisioning
-
Stream/export sensor data from baremetal and visualize it in grafana
- some potentially-useful resources to explore:
- https://grafana.com/blog/2019/11/06/how-to-stream-sensor-data-with-grafana-and-influxdb/
- https://grafana.com/blog/2021/08/12/streaming-real-time-sensor-data-to-grafana-using-mqtt-and-grafana-live/
- https://grafana.com/blog/2024/01/03/how-to-create-alert-rules-to-monitor-sensor-data-with-grafana-and-raspberry-pi/
- https://github.com/sbnb-io/sbnb/blob/main/README-GRAFANA.md
- https://grafana.com/grafana/dashboards/237-sensors/
- https://github.com/ncabatoff/sensor-exporter
- some potentially-useful resources to explore:
-
Automate firmware updates
-
Test zapping devices and make sure it works fine. Current version was tested on
draupnir, which seemed to wipe the disk fine, but I had to do some manual steps afterwards to create OSD on the new node once the node was re-provisioned and joined the cluster again. -
Install ARA to record ansible executions
-
Try rke2 which includes by default Cilium and nginx-ingress + etcd db
-
Replace kickstart with cloud-init
- Kickstart is gonna end (?) and cloud-init is much more agnostic; also it can trigger ansible pull
- Cloud-init can also be used across muppet OSes, not just Fedora, as is the case with kickstart
- ref: https://github.com/khuedoan/homelab/issues/179#issue-2875515756
-
Improve github+gitea workflow
- One of the downsides is that I need to delete non-master (PR) branches on gitea manually (well, via cli, but still)
- Maybe use a separate branch for gitea? E.g.
main? - Or maybe use a separate remote for gitea (or for github? since gitea is technically considered "the origin"?) E.g.
gitea(orgithub, for github repo origin)?
-
Encrypt kubeconfig with sops so it can be committed to git
-
Update architecture/overview components
- Basic diagram of code components and their relations
- Description of components and their purpose
-
Update concepts/pxe_boot with a visual "in-action" showcase of how it works, once it's in place
-
Add up-to-date config files of C1111 and C3560 for reference
- Can be placed in a separate note (probably don't even need to make it visible in nav menu) and referenced from installation/production/network
-
Check that devices on Guest WiFi network (when Eero is in AP/Bridge mode!) are still isolated and cannot see or communicate with each other or the main network.
- Eero in Bridge mode looses a lot of security related functionality (it becomes "greyed out" in the app also.) However, it seems that the guest network can still be enabled from the app. Hopefully that guest network is still isolated, but needs double-checking.
- Some related links:
-
When storing terraform state locally one needs to think about where/how to back it up. An alternative would be to use terraform cloud or opentofu TACOS, which are paid services (Plus your state is stored on someone else's computer, and hence should be encrypted)
- What can be alternatives to storing the state locally?
- Initial provisioning can be done with local state
- Once the cluster is up and running, we can host Atlantis and migrate the state to it.
- As an added benefit, this makes it possible to run terraform from PRs
- Store/commit sops-encrypted state. Run
terraformwith a script/make wrapper that decrypts the state before runningterraformcommands, and re-encrypts it at the end.
- Initial provisioning can be done with local state
- What can be alternatives to storing the state locally?
-
Configure
/etc/hostson local controller machine as part ofmetalprovisioning# midgard.local homelab # network devices 10.10.10.1 muspell 10.10.10.2 bifrost # k8s cluster 10.10.10.10 odin 10.10.10.11 freyja 10.10.10.12 heimdall 10.10.10.20 mjolinr 10.10.10.21 gungnir 10.10.10.22 draupnir 10.10.10.23 megingjord 10.10.10.24 hofund 10.10.10.25 gjallarhorn 10.10.10.26 brisingamen # storage devices 10.10.10.30 yggdrasil -
Configure
~/.ssh/configon local controller machine as part ofmetalprovisioningHost 10.10.10.* StrictHostKeyChecking no LogLevel ERROR UserKnownHostsFile /dev/null # muspell (C1111 router) in homelab vlan Host 10.10.10.1 muspell User cisco PasswordAuthentication yes # bifrost (C3560 switch) in homelab vlan Host 10.10.10.2 bifrost User cisco PasswordAuthentication yes KexAlgorithms +diffie-hellman-group14-sha1 HostKeyAlgorithms +ssh-rsa # k8s cluster nodes in homelab vlan Host 10.10.10.1* 10.10.10.2* odin freyja heimdall mjolnir draupnir gungnir megingjord hofund brisingamen gjallarhorn User root IdentityFile ~/.ssh/homelab_id_ed25519 StrictHostKeyChecking no LogLevel ERROR UserKnownHostsFile /dev/null GSSAPIAuthentication no # not supported on OS I use today for servers # storage nodes in homelab vlan Host 10.10.10.3* yggdrasil User root IdentityFile ~/.ssh/homelab_id_ed25519 StrictHostKeyChecking no LogLevel ERROR UserKnownHostsFile /dev/null -
Check if server is up before sending WoL magic packets
-
Ask before proceeding when running
make bootstrapinmetalprovisioning- The server prefers to boot from Network when woken up, which will erase all data on the disk and re-install the OS
- Ask the user for confirmation before proceeding.
- Mention
make wakealternative which can be used just to wake up the machines
- Mention
-
Consider restricting ssh access from homelab to router/switch SVI to specific IPs
- [ ] Limit permits to specific IP addresses instead of using! --- Define ACL for traffic FROM Homelab Network --- ip access-list extended ACL_FROM_HOMELAB_NETWORK ! ... ! (Optional: Add permits if Homelab needs to SSH to router's Homelab SVI - local management when e.g. laptop is physically connected to homelab network) 103 remark Permit SSH from Homelab to router's Homelab SVI (local management) 103 permit tcp 10.10.10.0 0.0.0.255 host 10.10.10.1 eq 22 104 remark Permit SSH from Homelab to switch's Homelab SVI (local management) 104 permit tcp 10.10.10.0 0.0.0.255 host 10.10.10.2 eq 22 199 remark --- END --- ! ... exit10.10.10.0so that e.g. k8s servers couldn't ssh to Homelab's router or switch -
Provision cisco devices with Ansible
-
Explore Enchanced Power Saving Mode in BIOS
- Newer Lenovo machines support enhanced power saving mode which lowers power consumption during power-off.
- Won't do: WoL is not supported!
-
Configure and document BIOS -> Power -> After Power Loss
- What option is better for my use-cases? Make sure it's configured everywhere and document it.
-
Figure out why dnf is very slow
- References:
- Seems like adding
fastestmirror=Trueto/etc/dnf/dnf.confhelps at least to some degree
-
Setup pi-hole on the cluster
-
Storage Node / NFS Optimizations
- NFS mount monitoring/health check on K8s side There's no monitoring or alerting for NFS mount failures on the K8s side. If the NAS goes down, pods will hang on NFS operations. This is a common NFS issue. Consider adding:
- NFS mount soft option (instead of default hard) for timeout behavior
- A health check pod or monitoring rule
- NFS server tuning For a 4-drive array serving media to a K8s cluster, you might want to tune:
- NFS thread count (/proc/fs/nfsd/threads, default is 8)
- Read/write size in mount options This is optional and premature optimization, but should be kept in mind.
- fstab entry cleanup
- If you remove a data drive from inventory, the fstab entry persists (uses lineinfile with state: present, never absent). Not a concern for initial deployment.
- Use
nftablesinstead ofufwon Debian- Add equivalent
firewalldtasks to properly support RedHat
- Add equivalent
- Monitoring of SMART data
- Parse the data in the systemd-sheduled script and put it to sqlite DB
- Create a dashboard and alerts in grafana
- Profit ...or not, depending on how lucky you are with the disks, but at least you'll be notified about issues
-
With snapraid it's a good idea to exclude unimportant files that regularly change. Jellyfin metadata for example.
-
NFS branch review follow-ups Non-blocking findings from the pre-merge code review of the
nfsbranch (2026-04-21, review doc was local-only). Deferred as non-blocking for the merge (no correctness findings), but the stack is untested end-to-end: NAS-side ansible was applied ~Feb 2026 (pre-dating the Feb 21 storage-firewall and Mar 25 router-ACL commits), and the K8s side (csi-driver-nfs, PV/PVC binding, Jellyfin NFS mount) has never run — the nodes have been powered off since. First boot after the merge is the real validation: rerunmake metalto reconcile NAS drift, then./tests/metal.shand the storage role's NFS validation tasks.- Move the Videos PV out of the Jellyfin chart — done pre-merge: static NFS PVs are now rendered from
nfs.volumesinsystem/csi-driver-nfs(values +templates/pv-nfs.yaml); app charts keep only the namespaced PVC bound viavolumeName+storageClassName: "". Future shares (Pictures, Documents, Music, Backups) are one values entry each. - Replace hardcoded NAS IP with a DNS name —
system/csi-driver-nfs/values.yaml(nfs.server) uses10.10.10.30directly; breaks silently if the NAS IP ever changes. Add a DNS record for the NAS and reference it by name. - Add resources requests/limits to Jellyfin chart containers — all 6 containers (jellyfin, transmission, prowlarr, radarr, sonarr, jellyseerr) run
BestEffortwith no requests/limits; Jellyfin transcoding is CPU/memory-heavy and an OOM can destabilize the node. Start conservative (jellyfin: 500m/1Gi request, 2Gi memory limit; arrs: 100m/256Mi/512Mi) and tune. Also verify readiness/liveness/startup probes —app-templatedefaults may not fit Jellyfin's slow cold start. Do this before the first cluster boot — the setup has never actually run, so there is no operational evidence thatBestEffortis tolerable here. -
hostPath: /dev/driin Jellyfin chart — works for QuickSync transcoding but bypasses Pod Securityrestrictedand pins the pod to GPU nodes implicitly. Prefer the Intel GPU device plugin (https://github.com/intel/intel-device-plugins-for-kubernetes) advertisinggpu.intel.com/i915; otherwise document the justification inline and pin vianodeSelector. - Helm chart pinning — decision (2026-08-18): keep the status quo (exact pins + Renovate; no
Chart.lock, no vendoring). Lifted to AD-0008 — see it for full rationale and accepted residual risk. Future code reviews should not re-flag missingChart.lockfiles. - Create the
nfs-csi-videosStorageClass or drop the reference — resolved with the PV move:storageClassNamewas dropped from the PV entirely and the PVC binds statically viavolumeName+storageClassName: ""; no phantom class remains. - Protect
smtp_passwordfrom leaking —metal/roles/storage/defaults/main.ymlrelies on a "use vault!" comment only. Addno_log: trueto the msmtp template task and document ansible-vault usage in the role. - Minor cleanups from the same review:
metal/roles/storage/tasks/disks.ymlmixesansible.posix.mount(data drives) and the deprecated baremountmodule (parity) — standardize onansible.posix.mountmetal/roles/storage/tasks/seachest_utils.ymlparses SMART output with brittle regexes that silently degrade to-1/0on format changesmetal/roles/storage/defaults/main.ymlhardcodes workstation IP192.168.1.100/32in role defaults — belongs in inventory/host_varsmetal/roles/storage/tasks/k8s_validation.ymlpipes shell intokubectl apply— preferkubernetes.core.k8smodule
- Move the Videos PV out of the Jellyfin chart — done pre-merge: static NFS PVs are now rendered from
-
Investigate and fix CGNAT / Double NAT from ISP
- Problem: ISP assigns a CGNAT address (
100.64.0.0/10range) instead of a public IP, resulting in double NAT (ISP NAT + C1111 NAT). This can affect P2P/torrents (reduced peer connectivity), some VOIP providers, and gaming (strict NAT type). - How to detect:
- Check router WAN IP:
show ip interface GigabitEthernet0/0/0 | include Internet address— if it's in the100.64.0.0/10range (CGNAT reserved per RFC 6598), you're behind CGNAT - Compare WAN IP to public IP:
show ip interface GigabitEthernet0/0/0vs whatcurl ifconfig.mereturns from an internal host — if they differ, you're behind CGNAT - Traceroute:
traceroute 8.8.8.8from the router — extra hops in the100.64.0.0/10range confirm CGNAT
- Check router WAN IP:
- Possible solutions:
- Request a public IP from ISP — simplest fix; some ISPs offer it for free, others charge a small monthly fee. Ask for a static public IPv4 address
- Request a dynamic public IP — cheaper alternative; you still get a real public IP, it just changes occasionally. Works well with DDNS (though less relevant since external access uses Cloudflare tunnels)
- Use IPv6 — if ISP provides native IPv6, services can be reached directly over IPv6 without NAT. Requires configuring IPv6 on C1111 and firewall rules for it
- DS-Lite / MAP-E — some ISPs offer transition mechanisms that give you a public IPv4 via tunneling over IPv6. Check if your ISP supports this
- VPN with port forwarding — services like Mullvad or AirVPN provide a VPN with a dedicated port forward, bypassing CGNAT for specific services (useful for torrents)
- Current impact assessment:
- Cloudflare tunnels: not affected (outbound-initiated)
- Web browsing / streaming: not affected
- Torrents: reduced peer connectivity (can't accept inbound connections from full swarm)
- VOIP: mostly fine with modern providers using STUN/TURN
- Gaming: possible strict NAT issues with matchmaking