Running 15+ Production Services on a Notebook: My HomeLab Setup
Updated June 28, 2026
My HomeLab runs on a single notebook 24/7 — a recycled HP Pavilion with 8GB RAM — and it powers 15+ services including this blog. Here’s how it’s built, and how I’ve been hardening it as I go.
The Stack
- Hypervisor: Proxmox VE
- Containers: Portainer (stable services) + Dokploy (new deploys, host mode)
- Networking: Cloudflare Tunnel (zero open ports) + Tailscale VPN for admin access
- DNS: Cloudflare, with Terraform management in progress
- Observability: Prometheus + Grafana + Jaeger + OpenTelemetry Collector
- Automation: n8n + a custom MCP server for AI-agent administration
Why This Matters: Running everything locally gives me complete control over my data and infrastructure. No cloud bills for staging environments, no vendor lock-in for experimental services. Cloudflare Tunnel keeps the attack surface at zero while still exposing what needs to be public; Tailscale handles everything administrative that shouldn’t be.
Engineering Decision: Why Not Docker Swarm?
One real engineering decision worth mentioning: I initially tried Docker Swarm for orchestration, but hit a wall — Swarm’s IPVS-based routing mesh doesn’t work inside unprivileged LXC containers on Proxmox.
Rather than fight the kernel, I moved stable services to Portainer (standalone/compose) and adopted Dokploy for new deploys, migrating services over progressively.
Security Audit: Fixing My AI Agent
The Problem: I built a custom MCP server that lets AI agents manage this homelab directly: checking container status, starting/stopping services, pulling logs. When I reviewed it properly, I found the agent was connecting as root, directly to the Proxmox hypervisor, with a tool that accepted arbitrary shell commands with no input validation.
That’s a real risk, not a hypothetical one — full host compromise if the MCP server itself were ever exploited.
The Fix:
- Replaced root/hypervisor access with a non-root user scoped to the container itself
- Removed the arbitrary-shell-command tool entirely
- Added an allowlist for every operation that takes a container name, with input sanitization
- Added structured audit logging for every tool call
The whole fix is documented as an ADR. The Docker socket inside the container is still root-equivalent — that’s a known, accepted risk of how Docker works, not something I’m pretending is solved.
Troubleshooting Done
OTEL collector stuck in restart loop: deprecated exporter `otlp_grpc` removed in collector v0.103.0. Fixed by upgrading the config to the new exporter name — now running stable with traces from AI agent sessions confirmed landing in Jaeger. The full pipeline is healthy: agent action → OTLP → collector → Jaeger.
Next Up
- Terraform DNS reconciliation (state divergence detected during plan)
- Ansible playbook execution (3 investigation specs written)
- Docker socket risk assessment
Production Services (15+ Running 24/7)
Core Infrastructure
| Service | Purpose |
|---|---|
| n8n | Workflow automation (public) |
| Dokploy | PaaS platform (public) |
| erickguedes.com | Personal portfolio site (SSR) |
| Wiki (MkDocs) | Internal documentation |
Media Stack
| Service | Purpose | Access |
|---|---|---|
| Jellyfin | Media streaming | LAN only |
| qBittorrent | Torrent client | LAN only |
| RSS aggregators | Content feed aggregation | LAN only |
Observability Stack
| Service | Purpose |
|---|---|
| Grafana | Dashboards & alerts |
| Prometheus | Metrics collection |
| Jaeger | Distributed tracing |
| OpenTelemetry Collector | OTLP HTTP receiver |
Additional Services
| Service | Purpose | Access |
|---|---|---|
| Portainer | Container management | Tailscale only |
| Cloudflared | Tunnel agent | LAN only |
Architecture Highlights
Zero Open Ports
Implementation: Cloudflare Tunnel configured to route only necessary services to the internet. Router has zero ports forwarded.
Result: Attack surface is effectively zero. Cloudflare handles all ingress, TLS, DDoS protection.
Non-Root Container Execution
Problem: Docker by default runs containers as root. In a security-conscious environment, this is unacceptable.
Solution: Container runs as non-root user (1000) inside LXC, with bind mounts restricted to container’s home directory.
Tradeoffs:
- Can’t run some root-only Docker operations (depends on how container is started)
- More complex configuration for volumes
Status: Production, stable, audited
Docker Socket Risk
Problem: If container can access Docker socket, it can run arbitrary commands as root on the host.
Solution: Containerized Docker daemon (docker:dind) with scoped access, no host socket exposed.
Status: Production, documented risk, mitigation in progress
Self-Automation: Custom MCP Server
What It Does
Builds a custom MCP server that allows AI agents to manage infrastructure directly:
- Operations: Check status, start/stop/restart containers, view logs
- Security: Scoped to container level, no root access to Proxmox
- Hardening: Allowlist-based, input sanitization, audit logging
- Integrations: Traces send to Jaeger via OpenTelemetry
The Fix (Security Audit)
Initial implementation had two critical issues:
- Root access: Agent connected as root to Proxmox hypervisor (full host compromise risk)
- Arbitrary shell execution: Tool accepted arbitrary shell commands with no input validation (RCE risk)
Implemented Fix:
- Replaced root/hypervisor access with non-root user scoped to LXC container
- Removed arbitrary shell execution entirely
- Implemented allowlist for all operations (container names, actions)
- Added structured audit logging for every tool call
The whole fix is documented as an ADR. The Docker socket inside the container is still root-equivalent — that’s a known, accepted risk of how Docker works, not something I’m pretending is solved.
Real-Time Alerting: Prometheus → Alertmanager → n8n → Telegram
Complete Alert Pipeline:
Prometheus → Alertmanager (9093) → n8n Webhook → Telegram Bot → Me (and others)
Benefits
- No automated incident response: ✅ Alerts reach Telegram automatically
- No human delay: ✅ Get notified in minutes, not hours
- 24/7 coverage: ✅ Don’t miss critical alerts during off-hours
- Accountability: ✅ Know exactly when alerts fire and who gets notified
7 Automated Alert Rules
| Rule | Condition | Severity |
|---|---|---|
| High Error Rate | rate(agent_error_rate[5m]) > 0.1 for 2m | Warning |
| High LLM Call Count | sum(agent_llm_calls_total) > 100 for 5m | Warning |
| Slow Tool Latency | histogram_quantile(0.95, rate(agent_tool_latency_seconds_bucket[5m])) > 2 for 5m | Warning |
| Token Usage Spike | increase(agent_token_usage[1h]) > 10000 for 1h | Info |
| Critical LLM Failure | rate(agent_llm_calls_total{status="error"}[5m]) > 0.5 for 1m | Critical |
| No Tool Calls | agent_tool_calls_total == 0 for 10m | Info |
| Agent Session Timeout | agent_session_duration > 3600 for 5m | Info |
n8n Workflow
- Parsing: n8n parses alerts, saves to database
- Structured data: Better alert formatting and routing
- Notifications: One-click Telegram integration
Infrastructure as Code
Terraform (Cloudflare DNS)
Managed Cloudflare records with Terraform scripts. Currently working on state divergence handling and automation.
Ansible (Container Management)
Container management playbooks, updates, and backups. Currently validating playbook execution (3 investigation specs written).
What’s Next
Immediate Priorities
- Terraform DNS reconciliation: State divergence handling, automation for 100+ Cloudflare records
- Ansible playbook validation: 3 investigation specs written, execution pending
- Docker socket risk assessment: Documented, mitigation in progress
- OTEL agent tracing activation: Extending tracing from workflow nodes to AI agent sessions
Future Roadmap
| Feature | Status | Details |
|---|---|---|
| Kubernetes migration | Research | LXC → K3s pilot environment |
| Enhanced MCP agent capabilities | In Progress | More granular operations, self-healing |
| Advanced monitoring | Planned | Distributed tracing for all services, anomaly detection |
| Disaster recovery testing | Planned | Backup validation, restore procedures |
| Service mesh (Istio/Linkerd) | Future | Production-grade service-to-service security |
Built with:
Proxmox VE, Docker, Kubernetes, Terraform, Ansible, Cloudflare, Tailscale, n8n, OpenTelemetry, Prometheus, Grafana, Jaeger, MCP
Infrastructure status: 15+ services running 24/7, production-grade reliability, actively maintained and evolving
Erick Guedes
AI · SaaS · Sales Engineering · Solutions Consulting. Turning complex processes into scalable solutions.