Everything I've got running across two machines. Some of it took way longer than it should have. Tap read the story on any card for the diagram and the actual context.
π¬
Self-Hosted Media Stack
Full *arr stack: Jellyfin for streaming, Radarr + Sonarr for automated movie/TV downloads, Prowlarr for indexer management (11 indexers), qBittorrent tunneled through Gluetun VPN, Jellyseerr for request management, Tdarr for GPU-accelerated H.265 transcoding, Decluttarr for stalled download cleanup.
you ββΆ Jellyseerr ββΆ "I want this"
β
βββββββ΄ββββββ
Radarr Sonarr β the *arrs
βββββββ¬ββββββ
Prowlarr βββ 11 indexers
β hands off to
qBittorrent
β ALL traffic via
Gluetun VPN βββΆ internet
β lands on
NAS ββΆ Tdarr (H.265) ββΆ Jellyfin ββΆ TV
This is the project that started the whole homelab, honestly. I just wanted Plex without paying for Plex, and it kind of snowballed into a full automation pipeline.
The flow: I ask for something in Jellyseerr, Radarr or Sonarr go grab it, Prowlarr feeds them indexers, and qBittorrent does the actual download β but only ever through the Gluetun VPN, so if the tunnel drops, nothing leaks. Tdarr transcodes everything to H.265 on the iGPU overnight to save space, and Jellyfin serves it to the TV.
The part that took way too long was the VPN kill-switch and getting qBittorrent to talk to the *arrs through Gluetun's network β one wrong firewall port and the whole queue silently stalls. I've rebuilt that container combo more times than I'd like to admit.
π
Zero-Trust Access & Security
Vaultwarden (self-hosted Bitwarden) with Argon2id admin token and disabled registration. Nginx Proxy Manager handling all .lan domains with wildcard self-signed cert plus Let's Encrypt certs via Cloudflare DNS-01. Cloudflare Tunnels for public exposure with no open ports. Tailscale subnet routing for remote access. Fail2ban blocking brute-force attempts on exposed services.
VaultwardenNPMCloudflare TunnelTailscaleFail2ban
internet
β no ports forwarded. ever.
βΌ
Cloudflare Tunnel ββΆ NPM (TLS) ββΆ services
β
me, away ββΆ Tailscale ββΆ the LAN
secrets ββΆ Vaultwarden (Argon2id, signups off)
noise ββΆ Fail2ban ββΆ banned
The rule I set for myself: nothing on this network has a port forwarded on the router. Ever. If something's reachable from outside, it's because a Cloudflare Tunnel reached out to do it, or I'm on Tailscale.
Everything internal goes through Nginx Proxy Manager with TLS, passwords live in my own Vaultwarden (registration disabled, Argon2id on the admin token), and Fail2ban watches anything exposed and bans the obvious brute-force noise.
Being a security student, this is the part I actually care about getting right. It's a lot easier to reason about "zero open ports" than to constantly second-guess what's listening.
π‘
Home Automation Bridge
Eclipse Mosquitto MQTT broker enabling smart home devices (IR blasters, sensors) to communicate with Home Assistant. Auth-required with password file. Bridges a LinknLink eRemote to Home Assistant for IR control.
MosquittoMQTTHome AssistantIoTLinknLink
IR / RF signals
β
LinknLink eRemote
β publishes
Mosquitto (MQTT, auth required)
β subscribes
Home Assistant ββΆ automations fire
Smaller project, but a fun one. I've got a LinknLink eRemote that blasts IR, and I wanted Home Assistant to control the dumb IR stuff β the AC, some old gear β like it was smart.
Mosquitto sits in the middle as the MQTT broker, auth required, because an open broker on your network is basically a free microphone for anyone who finds it. The device publishes, Home Assistant subscribes, automations fire.
Nothing flashy, but it's the kind of glue that makes the whole "smart home" thing actually work instead of being five apps that refuse to talk to each other.
π
Monitoring & Observability
Uptime Kuma monitoring 19 services at 60-second intervals with Docker socket access for real-time container state. cAdvisor exporting container metrics for Prometheus on the SIEM machine. node-exporter providing host-level CPU, memory, and disk metrics. All data flows into Grafana dashboards on a dedicated monitoring server.
You can't fix what you can't see, so this layer exists so I'm not flying blind. Uptime Kuma pings 19 services every 60 seconds and yells at me the second one drops. cAdvisor and node-exporter ship container and host metrics over to Prometheus on the SIEM box, and Grafana turns all of it into dashboards.
The honest reason it exists: I got tired of finding out something was down by trying to use it and getting an error. Now I usually know before anyone else in the house does.
βοΈ
Self-Hosted Knowledge Sync
Obsidian LiveSync via CouchDB keeps notes synced across devices in real time, replacing the $5/month Obsidian Sync subscription. The NAS handles media and backup storage while notes sync through a dedicated CouchDB backend.
Obsidian LiveSyncCouchDBNAS / NFSNotes
phone ββ
laptop ββΌββΆ Obsidian LiveSync ββΆ CouchDB
desktop ββ real-time β
stays on my hardware
(goodbye $5/mo Obsidian Sync)
I take a lot of notes β homelab stuff, CTF writeups, class β and I didn't love paying $5 a month for Obsidian Sync when I've got a perfectly good server sitting right there.
So I stood up CouchDB and pointed Obsidian LiveSync at it. Now my vault syncs across every device in real time, the data never leaves my hardware, and the subscription's gone. Small win, but it's exactly the kind of thing self-hosting is perfect for.
ποΈ
NAS & Storage Infrastructure
Synology DS214 NAS with ~907GB usable storage mounted via NFS v3 to the Docker host. Hosts media, downloads, and backup storage. Proxmox backup target via a separate NFS export. fstab-mounted with _netdev and nofail for clean boot behavior.
Everything heavy lives on an old Synology DS214 β media, downloads, backups β mounted over NFS to the Docker host. About 907 GB usable, which fills up faster than you'd think once the *arrs get going.
The "took longer than it should have" award goes to the fstab mount options. Without _netdev and nofail, the VM would hang on boot waiting for a NAS that wasn't ready yet. Learned that one the hard way after a reboot left the whole thing stuck on a black screen. Proxmox backs up to a separate export on the same box.
π
Automation & Maintenance
Watchtower auto-updating all labeled containers nightly. Recyclarr syncing TRaSH Guides quality profiles to Radarr/Sonarr daily. Host OS auto-updates via unattended-upgrades with a Discord webhook notifying on success/failure, rebooting in the early morning if needed.
Watchtower ββΆ pulls new images nightly
Recyclarr ββΆ syncs TRaSH quality profiles
unattended-upgrades ββΆ patches the host OS
β on every run
Discord webhook ββΆ green / orange / red
The goal here is to do as little manual maintenance as possible. Watchtower updates containers overnight, Recyclarr keeps my Radarr/Sonarr quality profiles in line with the TRaSH guides, and the host patches itself with unattended-upgrades and reboots in the early morning if it has to.
The piece I'm weirdly proud of is the Discord webhook β every update run pings me with green, orange, or red so I know what happened without logging in. It took a couple of tries to stop it from false-alarming on every single run, but now it just quietly works.
π
DNS & Network Layer
AdGuard Home as the primary DNS server with ad blocking, custom .lan rewrite rules for all services, and DHCP-configured clients. All .lan domains resolve to the Docker host and route through NPM. DOCKER-USER iptables rules for container network security.
AdGuard HomeDNSiptablesNetworking
every device ββΆ AdGuard Home (DNS)
β blocks ads + trackers
β resolves *.lan names
βΌ
NPM ββΆ the right service
container traffic ββΆ DOCKER-USER iptables rules
AdGuard Home is the DNS for the whole house. It blocks ads and trackers network-wide and resolves all my internal names to the right place through the reverse proxy.
The catch with running the only DNS server on your network is that when it's unhappy, everything looks broken. I once spent an entire evening convinced AdGuard had died, when really the upstream resolver was timing out and a router reboot fixed it in thirty seconds. Lesson logged.
Container network rules are locked down through the DOCKER-USER iptables chain β never UFW, because UFW and Docker fight and Docker always wins.
π
Pentesting & CTF
Actively working through TryHackMe rooms and CTF competitions to practice offensive security. Hardware includes a Flipper Zero for wireless/RFID/IR research, a Pwnagotchi for wifi handshake capture, and a dual ESP32 setup for wireless experimentation. Daily drivers: Kali Linux, nmap, and gobuster.
Kali LinuxnmapgobusterTryHackMeCTFFlipper ZeroPwnagotchi
TryHackMe / CTFs ββΆ reps on the offensive side
βββββββββββββββββ the gear βββββββββββββββββ
β Kali Β· nmap Β· gobuster β
β Flipper Zero β RFID / IR / sub-GHz β
β Pwnagotchi β wifi handshakes β
β dual ESP32 β wireless tinkering β
βββββββββββββββββββββββββββββββββββββββββββββ
This is the "breaking things on purpose" half of the hobby, and the part that actually lines up with what I'm studying. I work through TryHackMe rooms and CTFs to get reps with the offensive side β recon, enumeration, the usual nmap-then-gobuster dance.
On the hardware side I've collected a Flipper Zero for RFID/IR/sub-GHz, a Pwnagotchi that quietly farms wifi handshakes, and a couple of ESP32s for wireless experiments. All of it on my own gear and my own networks β the whole point is understanding how the attacks actually work so I know how to defend against them.
π‘οΈ
SIEM & Security Monitoring
Dedicated machine running a full Wazuh stack (manager, indexer, and dashboard) for log ingestion and threat detection. Prometheus collects metrics from both machines via cAdvisor and node-exporter. Grafana ties it all together. Hands-on practice with SIEM/EDR concepts I'm studying in school.
This is the project I'm most invested in, because SIEM and EDR are exactly what I'm learning in school β so I built one instead of just reading about it. A dedicated machine runs the full Wazuh stack: manager, indexer, and dashboard, ingesting logs and flagging suspicious behavior across both boxes.
The live alert numbers on my dashboard page are pulled straight from this. Getting hands-on with real detections, tuning out the noise, and watching what actually trips the rules has taught me more than any slide deck. It's the closest thing to a real blue-team environment I can run at home.
π
This Website
Served from this homelab via a Cloudflare Tunnel. No cloud hosting, no monthly bill. The container count and live dashboard pull straight from the Portainer and Proxmox APIs.
AstroDockerCloudflare TunnelNode.js
visitor ββΆ Cloudflare Tunnel ββΆ this box
β
Astro (SSR / Node)
β pulls live
Portainer Β· Proxmox Β· Wazuh
no cloud host Β· no monthly bill
You're looking at it. This site is served straight off the homelab through a Cloudflare Tunnel β no cloud hosting, no monthly bill, just a container on the same box as everything else.
It's built with Astro and rendered on the fly by Node, which is why the container count and the live dashboard aren't screenshots β they're pulled from the Portainer and Proxmox APIs when the page loads, and the live page keeps refreshing itself after that.
The casino tab started as a joke and got completely out of hand, which is honestly the most accurate one-line summary of this entire hobby.