Projects

Self-directed infrastructure projects, built and running in a personal multi-node homelab.

Linux Home Server & Network Infrastructure

A Dell XPS 13 running Ubuntu Server serves as the always-on NAS and services host for the homelab, currently running seventeen Docker containers behind a Caddy reverse proxy that provisions and renews its own Let's Encrypt TLS certificates through the Tailscale daemon. The entire environment is reachable only across a Tailscale zero-trust mesh with no port forwarding, so no service is exposed to the public internet, and AdGuard Home runs as the mesh DNS resolver for network-wide ad and telemetry filtering. Administration is deliberately split across three tools rather than consolidated into one: CasaOS for container deployment, Cockpit for system-level and encrypted-drive management, and Portainer for direct Docker engine control.

Data protection follows a 3-2-1 model that pairs local volume mounts with cross-device Syncthing replication and AES-256 encrypted cloud backup through Rclone, monitored by SMART health checks and a Bash alerting layer that pushes any failure to a phone. The security baseline is key-only SSH hardened against upstream config drift, a default-deny UFW policy, and Fail2Ban. One representative problem from building it: a backup sweep left pointed at a stale path with no mount-state guard would, on its next run, have read the missing source as authoritative and deleted the matching cloud backup to match. Catching that before it ran, and extending a mount guard across every external-drive sweep so an absent drive becomes a logged no-op instead of a destructive event, is the kind of silent-failure auditing that keeps a backup system from quietly destroying the data it exists to protect.

Tech Stack: Ubuntu Server, Docker, CasaOS, Cockpit, Portainer, Caddy, Tailscale, AdGuard Home, Rclone, Syncthing, UFW, Fail2Ban, Bash

Virtualization & VM Infrastructure

A KVM/QEMU hypervisor managed through virt-manager runs a small fleet of purpose-built virtual machines on a private NAT network. virt-manager was chosen over the more common alternatives for specific reasons: GNOME Boxes produced unfixable SPICE display scaling bugs, and VirtualBox requires recompiling its kernel modules against Fedora's rolling kernel, which breaks periodically. The fleet includes a headless RHEL system built specifically for RHCSA preparation, installed with no GUI to match the exam environment and with the root account enabled to rehearse GRUB-based password recovery, a near-certain exam task; a Kali environment for graduate cybersecurity coursework; and a sandboxed malware-analysis VM disguised as a generic accounting workstation to defeat malware that suppresses its behavior when it detects security tooling.

Installing Docker on the same host broke internet access for the virtual machines, and fixing it required understanding why. Three independent nftables tables, belonging to firewalld, libvirt, and Docker, each register rules at the same kernel forwarding hook and evaluate traffic separately, so a packet that one table permits another can still drop, and correcting a single layer does nothing for the others. Restoring VM return traffic took two separate changes: an inter-zone policy for firewalld's own table, and a direct rule for the packet-filter table that Docker owns, where a Docker version change had left VM traffic with no matching accept rule. The permissive rule is safe because the machines sit behind NAT on a private subnet with no port forwarding, so nothing external can initiate a connection regardless of what the rule allows.

Tech Stack: KVM/QEMU, virt-manager, libvirt, firewalld, nftables, RHEL, Kali, Lubuntu, UEFI/Q35

Encrypted Storage Architecture

Three external drives are encrypted with LUKS2 and unlock automatically at boot from keyfiles stored on the already-encrypted root partition, registered in crypttab and mounted through fstab, with passphrases retained separately in a password manager for recovery. Devices are always identified by their LUKS UUID rather than by drive letter, since USB device letters reshuffle on every replug and only the UUID is a stable reference.

The most demanding problem in this area was a hard boot failure on the root volume itself. The machine dropped into an initramfs recovery shell across every boot entry with no passphrase prompt at all, and the generic error text pointed at a missing disk. The disk was fine: kernel logs showed the drive and all of its partitions detected within a second. The missing passphrase prompt was the real clue, because it meant the initramfs had never been given an unlock step for the root volume, which placed the fault in the crypttab-to-initramfs chain rather than in hardware. Four hardware and firmware theories were ruled out in sequence before the actual cause surfaced: the root volume's encryption entry had gone missing from crypttab entirely. Recovery was manual from the shell, followed by restoring the entry and rebuilding the initramfs, using the disappearance of one specific warning as the signal that the rebuild had actually taken rather than trusting that the command had merely exited cleanly.

Tech Stack: LUKS2, cryptsetup, crypttab, fstab, systemd-cryptsetup, initramfs, LVM

Linux Endpoint Administration & Automation

A Fedora Linux laptop serves as the primary workstation and is administered with the same rigor as the server. Bringing it to a stable state required kernel-level work: one GRUB parameter (amdgpu.sg_display=0) proved strictly necessary to stop the RDNA 3.5 GPU from crashing its scheduler and hard-locking the machine, a fix reasoned out from kernel behavior rather than found in documentation. Routine maintenance runs unattended through user-level systemd timers, among them a download-sorting daemon, a self-correcting DNS blocklist updater, and an Rclone cloud backup scheduled deliberately against the server's window so the two never compete for upstream bandwidth or API quota.

Running local AI inference on the workstation raised a concrete security question, because the model server has to bind broadly enough for a containerized frontend to reach it, which would otherwise expose its API on any public Wi-Fi the laptop joins. The answer was firewalld zone isolation: the port is removed from the public zone and bound only to the Tailscale and container-loopback zones, so the API answers mesh peers and the local container while staying invisible everywhere else. A separate break-glass container stack keeps the same tooling available offline during outages or travel. Malware scanning is intentionally not run on the laptop but consolidated onto the always-on server, since the workstation's working set already replicates into the server's scan scope and a machine that is frequently closed mid-scan is not a dependable host for a twelve-hour job.

Tech Stack: Fedora Linux, systemd (services, timers, drop-ins), firewalld, GRUB, Bash, Rclone, Docker, Ollama

Version Control & Deployment Infrastructure

A version-controlled Obsidian knowledge base is the source for this portfolio and syncs across devices over a Syncthing mesh, with one machine acting as a receive-only anchor that commits changes to a private GitHub repository on a scheduled cron job, from which the site builds and deploys to Vercel automatically on every push. The instructive failure here was an overnight build that broke against a commit history nobody had touched. A loose version range in the project's dependencies let a patch-level upstream update pull in on its own, which wiped Vercel's build cache and exposed a latent image-path bug the cache had been masking. Pinning dependencies to exact versions moved cache invalidation onto a controlled schedule rather than leaving it to fire whenever an upstream maintainer ships a patch, which is a production-reliability decision rather than a development convenience.

Tech Stack: Git, GitHub, Vercel, Astro, Markdown, Obsidian, Syncthing, cron, DNS