Documentation for my home lab CI/CD setup
This page documents issues, problems, and maintenance activities that occur during the operation of the home lab. Each entry includes the date, problem description, diagnosis steps, resolution, and follow-up notes.
The VIREN OVH VPS became completely unreachable — no ping, no SSH — after a period of normal operation. No external symptom beyond the box being “down”. The system was booting into emergency.target rather than completing a normal multi-user boot, which halted before networking or SSH ever started.
Initial hypothesis: firewall blocking connections — ruled out (no config check confirmed this).
Checked dnf history for a broken/interrupted package transaction — both relevant transactions showed Status: Ok, ruling out package corruption.
Noted a kernel upgrade (7.1.4-104 → 7.1.5-101) had occurred shortly before the outage via a manual dnf update (not the automatic security-only updater) and treated it as the leading suspect.
Rolled back the default boot kernel to 7.1.4-104 via grubby --set-default as a precaution — this did not resolve the issue on its own, indicating the kernel was not the actual cause.
Rescue-mode chroot sessions initially showed “No journal files were found” for the failed boot, which was misleading — persistent journal logging was in fact already configured and working on the real system; the gap was an artifact of chroot not reliably exposing the live journal state, not an actual logging failure.
Obtained a live boot capture via OVH’s KVM/serial console, which showed the boot reaching emergency.target roughly 40 seconds into boot, after a suspicious gap with no output — the actual diagnostic breakthrough.
Cross-referenced /etc/fstab inside a rescue-mode chroot and identified a stale UUID entry for a previously-attached music drive that had since been removed:
UUID=e2ce6651-9cda-4e34-a1b6-175704a7741e /mnt/music ext4 defaults 0 2
Since the device no longer existed and had no nofail option, systemd treated it as a required mount at boot, blocked on it, timed out, and dropped to emergency.target.
Note: Disk device naming (sda vs sdb) was inconsistent across separate OVH rescue-mode boots, requiring lsblk -f to be re-checked each time before mounting.
Commented out the stale /mnt/music line in /etc/fstab from within a rescue-mode chroot.
Switched OVH boot mode back to disk boot and rebooted; confirmed via KVM console that the system reached a normal login prompt without hitting emergency mode.
Confirmed external reachability (ping and SSH on the system’s non-default SSH port) after boot.
Re-tested the kernel upgrade path deliberately: set the default kernel back to 7.1.5-101 and rebooted again to confirm it boots cleanly now that the actual root cause was fixed. Confirmed working — the kernel was never the problem.
Monitoring the Ryzen NAS after the Global C-States fix was applied to ensure the freeze/sleep issues were fully resolved.
N/A
After several tests and a long period of uptime, system stability has been confirmed. The C-States fix successfully prevented the system from entering unrecoverable sleep states.
The system would occasionally enter a sleep state from which it could not awake, requiring a hard reset. This is a known issue with first-generation Ryzen processors and Linux.
Investigated system logs leading up to freezes. Identified that the freezes occurred during idle periods, pointing towards power management and C-States.
Disabled Global C-States in the BIOS/UEFI settings, which prevents the CPU from entering the low-power sleep states that cause the system to hang.
The NAS (Casper) needed more computing power for handling all tasks including CI/CD and self-hosted services.
N/A
Rebuilt the NAS hardware from an old AMD A10-7700K processor to an AMD Ryzen 1700 (repurposed old hardware) on AM4 using 16GB of DDR4 (previously 8GB of DDR3). Upgraded the OS drive from a 128GB SATA SSD to a 256GB NVMe SSD, and added two 1TB HDDs in a new ZFS pool called ‘casper-buffer’. During the hardware upgrade, upgraded from OMV7 to OMV8. Successfully exported and reimported the ZFS pools, and reassigned/setup the Gitea server. Added a local Gitea runner called ‘casper-runner’ to handle all CI/CD tasks with the new hardware capabilities.
Devices using Pi-hole DNS lost internet connectivity. The Pi-hole host could communicate with local network devices but could not reach external addresses such as 1.1.1.1. Investigation showed the system had a static IPv4 address configured via NetworkManager (nmcli), but no IPv4 gateway was defined. As a result, no default route existed and the system could not reach upstream DNS servers.
Confirmed Pi-hole host had a valid local IP address (192.168.0.125) on interface eth0.
ping 192.168.0.1
Result: Successful.
ping 1.1.1.1
Result: Failed.
ip route
Output showed only the local network route:
192.168.0.0/24 dev eth0 proto kernel scope link src 192.168.0.125 metric 100
The expected default route (default via 192.168.0.1) was missing.
sudo ip route add default via 192.168.0.1
Identified root cause: the NetworkManager connection used manual IPv4 configuration but did not define an ipv4.gateway value.
nmcli connection modify ipv4.gateway 192.168.0.1
nmcli connection up </code></pre>
Performed a Pi-hole gravity update and added DNS blocklists to prevent Meta Quest Pro devices from contacting Meta services, including firmware update and advertising endpoints. The Meta Quest Pro is discontinued, and updates are intentionally blocked at the DNS level.
pihole -g
https://raw.githubusercontent.com/ibrah3m/pihole-meta-quest-blocklist/main/hosts-firmware
https://raw.githubusercontent.com/ibrah3m/pihole-meta-quest-blocklist/main/hosts-ads
Ensured ENLIL, LORIC, AUREL, and Casper Raspberry Pi nodes are using the ENLIL Pi-hole as their DNS server directly on the devices rather than relying on the router settings.
nmcli device show
sudo nmcli connection modify connection ipv4.dns "192.168.0.125" ipv4.ignore-auto-dns yes ipv6.ignore-aut-dns yes ipv4.dns "2a00:23c7:593:6501:ba27:ebff:fe0f:e3f2"
nmcli connection show target_name
Changed the DNS resolving on Raspberry Pi nodes to target the ENLIL Pi-hole hosted on 192.168.0.125.
nmcli connection show
sudo nmcli connection modify BT-RJATFG ipv4.dns "192.168.0.125" ipv4.ignore-auto-dns yes && sudo nmcli connection up BT-RJATFG && nmcli connection show BT-RJATFG
Noted that our home network is on BT, which prevents directly overriding DNS for all devices. Devices currently need to be manually pointed to the Pi-hole.
docker run --rm alpine cat /etc/resolv.conf
This ensures that containers respect the Pi-hole DNS.
Deployment of Pi-hole on ENLIL (Raspberry Pi 1 Model B) as a dedicated network DNS and filtering node.
Verified Pi-hole operation via web interface and query logs.
Findings:
Installed and configured Pi-hole on ENLIL.
Added blocklists sourced from Firebog
Ran a manual Gravity update using the Pi-hole web interface.
Added manual blocking for Facebook-related domains.
Set upstream DNS provider to Cloudflare (temporary).
The /etc/hosts file was being reset after system reboots, losing custom DNS resolutions including 192.168.0.124 entries.
Investigated the cause of hosts file resetting:
cat /etc/cloud/cloud.cfg
cat /etc/cloud/templates/hosts.debian.tmpl
Findings: Cloud-init was managing /etc/hosts and overwriting custom entries on each boot.
Disabled cloud-init management of /etc/hosts and updated the template:
sudo nano /etc/cloud/cloud.cfg
Set: manage_etc_hosts: false
sudo nano /etc/cloud/templates/hosts.debian.tmpl
sudo reboot
The /etc/hosts file now retains custom entries across reboots.
LORIC (Raspberry Pi 3 B+ orchestration node) did not respond after a reboot. The device was unreachable over the network, and the Gitea runner service on it was offline.
ip a
ifconfig wlan0
Findings:
sudo raspi-config
sudo nano /etc/hosts
Added entries for all relevant nodes.
sudo systemctl restart gitea-runner
sudo systemctl status gitea-runner
LORIC reconnected to the network and the runner service resumed normal operation.