Correction, September 2026. This post originally described the server as wrapping 9 services with 32 tools, including a confirmation-gated write surface and plans for an SSE transport. The maintained server is simpler and stricter than that: local stdio plus a CLI, up to 25 read-only tools across 10 configured backends, and 15 documentation resources. There are no write tools and no remote transport. Lifecycle changes (provisioning, DNS records, container updates) are owned by Semaphore automation, and the IPAM backend is now Nautobot — NetBox is retained rollback-only. The sections below have been rewritten to describe the current architecture; the original narrative is preserved where it explains the design decisions that still hold.
Visibility into 50+ services requires centralized logging, proactive alerting, and dashboards. This wiki covers my monitoring stack and the patterns that make it work.
Monitoring Stack # Graylog Centralized Logging # Graylog is my log aggregation platform—collecting, processing, and visualizing logs from across the homelab.
Why an XDR in a Homelab? # When I first started building out my homelab infrastructure, I fell into the same trap that catches most homelab enthusiasts: I assumed that being behind a firewall made me safe. After all, I wasn’t running a Fortune 500 network. I had VLANs, I had a next-generation firewall doing deep packet inspection, and I kept my systems patched. What more did I need?
The Challenge # I needed a unified security monitoring solution that could:
Provide endpoint detection and response (XDR) capabilities Integrate with my existing Graylog centralized logging infrastructure Scale from a single-node deployment to multi-node if needed Work with my existing Hermes threat intelligence feeds The Solution # Wazuh Single-Node Stack # Deployed Wazuh as a Docker-based single-node stack. The single-node architecture includes:
The Problem # I noticed my Graylog VM was consuming more memory than expected. The VM has 4GB RAM allocated, but both Graylog and OpenSearch were competing for memory, leaving little headroom for the operating system’s filesystem cache.
What I Tried # First, I checked memory usage on the Graylog VM:
What Changed # Pinned Graylog’s JVM heap to 1GB by adding explicit GRAYLOG_SERVER_JAVA_OPTS to docker-compose.yml:
1 GRAYLOG_SERVER_JAVA_OPTS: "-Xms1g -Xmx1g -XX:NewRatio=1 -server -XX:+UseG1GC" Why # Noticed high memory usage on the Graylog VM. JVM ergonomics was auto-allocating ~2GB for Graylog, leaving insufficient RAM for OS filesystem cache. OpenSearch query performance suffers without cache headroom.