This article contains affiliate links. If you purchase through our links, we may earn a commission at no extra cost to you. We only recommend products we’ve thoroughly researched and verified.

Running a home server without monitoring is flying blind. When Plex stops transcoding, when disk space fills up, or when Home Assistant becomes unreachable — you want to know immediately, not hours later when someone complains. This guide sets up three monitoring layers: Netdata for real-time system metrics, Prometheus + Grafana for historical dashboards, and Uptime Kuma for service availability alerts.
What you need:
- A mini PC running Ubuntu Server 24.04 LTS with Docker installed
Layer 1: Netdata — Real-Time System Metrics
Netdata is the fastest path from zero to monitoring. One command installs the agent, and you get 1,000+ real-time metrics — CPU per-core, memory, disk I/O, network per-interface, Docker containers, and more — with a built-in web dashboard.
Install Netdata
# Official installer — runs on Ubuntu, Debian, Fedora, RHEL
wget -O /tmp/netdata-kickstart.sh https://get.netdata.cloud/kickstart.sh
sh /tmp/netdata-kickstart.sh --stable-channel --no-updates --disable-telemetry
The installer adds Netdata as a system service. Access the dashboard immediately at:
http://192.168.1.100:19999
No configuration needed for basic usage.
What Netdata Shows Out of the Box
- CPU: Per-core utilization, system calls, interrupts
- Memory: Used/buffered/cached breakdown, swap usage
- Disk: I/O throughput, IOPS, utilization per device
- Network: Per-interface throughput, packet rates, errors
- Processes: Top consumers by CPU and memory
- Docker: CPU and memory per container auto-detected
- System: CPU temperature, fan speed where supported
Configure Disk Space Alerts
Netdata alerts by default when disk usage exceeds 85%. Lower the threshold for your server:
sudo nano /etc/netdata/health.d/disk_space.conf
alarm: disk_space_usage
on: disk.space
lookup: average -1m unaligned of used
units: %
every: 1m
warn: $this > 75
crit: $this > 90
info: disk $label:mount is used
to: sysadmin
Send Netdata Alerts to Discord
sudo nano /etc/netdata/health_alarm_notify.conf
Find and set:
SLACK_WEBHOOK_URL="" # Leave blank
DISCORD_WEBHOOK_URL="https://discord.com/api/webhooks/YOUR-WEBHOOK"
DEFAULT_RECIPIENT_DISCORD="homeserver"
Restart: sudo systemctl restart netdata
Layer 2: Prometheus + Grafana — Historical Dashboards
Netdata’s free tier retains data for 24-48 hours. Prometheus stores metrics for weeks or months and feeds Grafana’s dashboards for trend analysis, capacity planning, and correlating events.
Deploy the Stack
mkdir -p ~/monitoring/{prometheus,grafana,node-exporter}
nano ~/monitoring/docker-compose.yml
services:
prometheus:
image: prom/prometheus:latest
container_name: prometheus
ports:
- "9090:9090"
volumes:
- ./prometheus/prometheus.yml:/etc/prometheus/prometheus.yml:ro
- prometheus_data:/prometheus
command:
- '--config.file=/etc/prometheus/prometheus.yml'
- '--storage.tsdb.path=/prometheus'
- '--storage.tsdb.retention.time=30d' # Keep 30 days
- '--storage.tsdb.retention.size=10GB' # Cap at 10GB
restart: unless-stopped
grafana:
image: grafana/grafana:latest
container_name: grafana
ports:
- "3000:3000"
volumes:
- grafana_data:/var/lib/grafana
environment:
- GF_SECURITY_ADMIN_USER=admin
- GF_SECURITY_ADMIN_PASSWORD=changeme
- GF_USERS_ALLOW_SIGN_UP=false
restart: unless-stopped
node-exporter:
image: prom/node-exporter:latest
container_name: node-exporter
ports:
- "9100:9100"
volumes:
- /proc:/host/proc:ro
- /sys:/host/sys:ro
- /:/rootfs:ro
command:
- '--path.procfs=/host/proc'
- '--path.rootfs=/rootfs'
- '--path.sysfs=/host/sys'
- '--collector.filesystem.mount-points-exclude=^/(sys|proc|dev|host|etc)($$|/)'
restart: unless-stopped
cadvisor:
image: gcr.io/cadvisor/cadvisor:latest
container_name: cadvisor
ports:
- "8080:8080"
volumes:
- /:/rootfs:ro
- /var/run:/var/run:ro
- /sys:/sys:ro
- /var/lib/docker/:/var/lib/docker:ro
- /dev/disk/:/dev/disk:ro
privileged: true
restart: unless-stopped
volumes:
prometheus_data:
grafana_data:
Configure Prometheus Scrape Targets
nano ~/monitoring/prometheus/prometheus.yml
global:
scrape_interval: 15s
evaluation_interval: 15s
scrape_configs:
- job_name: 'prometheus'
static_configs:
- targets: ['localhost:9090']
- job_name: 'node-exporter'
static_configs:
- targets: ['node-exporter:9100']
- job_name: 'cadvisor'
static_configs:
- targets: ['cadvisor:8080']
- job_name: 'netdata'
metrics_path: /api/v1/allmetrics
params:
format: [prometheus]
static_configs:
- targets: ['192.168.1.100:19999'] # Your server IP
Start the stack:
cd ~/monitoring
docker compose up -d
Access:
- Prometheus:
http://192.168.1.100:9090 - Grafana:
http://192.168.1.100:3000
Import Grafana Dashboards
In Grafana:
-
Add Prometheus data source: Configuration → Data Sources → Add → Prometheus → URL:
http://prometheus:9090 -
Import community dashboards:
-
Node Exporter Full ID: 1860 — system metrics, CPU, memory, disk, network
-
cAdvisor ID: 14282 — Docker container metrics per container
- Netdata — search Grafana.com for current IDs
Dashboards → Import → Enter ID → Load → Select Prometheus datasource → Import
Within minutes you have production-quality dashboards showing weeks of trend data.
Layer 3: Uptime Kuma — Service Availability Alerts
Uptime Kuma monitors URLs and TCP ports and sends alerts when they go down. Essential for knowing when a Docker container crashes, a service stops responding, or your router loses connectivity.
Deploy Uptime Kuma
mkdir -p ~/uptime-kuma
nano ~/uptime-kuma/docker-compose.yml
services:
uptime-kuma:
image: louislam/uptime-kuma:latest
container_name: uptime-kuma
ports:
- "3001:3001"
volumes:
- uptime_kuma_data:/app/data
restart: unless-stopped
volumes:
uptime_kuma_data:
cd ~/uptime-kuma
docker compose up -d
Access at http://192.168.1.100:3001. Create an admin account on first visit.
Add Monitors
In Uptime Kuma → Add New Monitor:
For HTTP services:
- Monitor Type: HTTP s
- URL:
http://192.168.1.100:8096Jellyfin - Name: Jellyfin
- Heartbeat Interval: 60 seconds
For network services DNS, MQTT, etc.:
- Monitor Type: TCP Port
- Hostname:
192.168.1.100 - Port:
53 - Name: Pi-hole DNS
Useful monitors to add:
| Service | Type | URL/Address |
|---|---|---|
| Home Assistant | HTTP | http://192.168.1.100:8123 |
| Jellyfin | HTTP | http://192.168.1.100:8096 |
| Nextcloud | HTTP | https://nextcloud.yourdomain.com |
| Portainer | HTTP | https://192.168.1.100:9443 |
| Pi-hole | TCP Port | Port 53 |
| Vaultwarden | HTTP | https://vault.yourdomain.com |
| Router | PING | 192.168.1.1 |
Configure Discord Notifications
- Settings → Notifications → Add Notification
- Type: Discord
- Discord Webhook URL: paste from your Discord server’s Integration settings
- Apply to all monitors or select specific ones
When any monitored service goes down, Uptime Kuma sends an alert to Discord within 60-120 seconds.
Complete Monitoring Stack Resource Usage
| Service | RAM usage | CPU at idle |
|---|---|---|
| Netdata | ~100MB | <0.5% |
| Prometheus | ~100-200MB | <0.5% |
| Grafana | ~150-300MB | <0.2% |
| node-exporter | ~15MB | <0.1% |
| cAdvisor | ~50-100MB | <0.5% |
| Uptime Kuma | ~50MB | <0.1% |
| Total | ~500-750MB | ~1-2% |
On the EQ14 with 16GB, this monitoring stack uses 4-5% of available RAM — leaving 15GB+ for other services. On the MINI S12 with 8GB, consider running Netdata alone plus Uptime Kuma total ~200MB rather than the full Prometheus/Grafana stack.
Useful Grafana Queries
Once Prometheus is collecting data, you can write queries in Grafana for custom panels:
# CPU usage percentage
100 - (avg by(instance) (rate(node_cpu_seconds_total{mode="idle"}[5m])) * 100)
# Memory used GB
(node_memory_MemTotal_bytes - node_memory_MemAvailable_bytes) / 1024 / 1024 / 1024
# Disk usage percentage for /
1 - (node_filesystem_avail_bytes{mountpoint="/"} / node_filesystem_size_bytes{mountpoint="/"})
# Network receive speed in MB/s
rate(node_network_receive_bytes_total{device="enp2s0"}[5m]) / 1024 / 1024
# Docker container memory usage
container_memory_usage_bytes{container_label_com_docker_compose_project!=""}
Troubleshooting
Netdata not collecting Docker metrics
Ensure the Netdata user has access to the Docker socket: sudo usermod -aG docker netdata then restart: sudo systemctl restart netdata.
Prometheus not scraping node-exporter
Check the node-exporter is reachable: curl http://192.168.1.100:9100/metrics. Verify the Prometheus config YAML is valid — indentation errors are common.
Grafana shows “No Data”
Check the Prometheus datasource connection in Grafana → Configuration → Data Sources → Test. Ensure the scrape job name in prometheus.yml matches what the dashboard queries expect.
Who Should Skip This Comparison
Frequently Asked Questions
What is the best monitoring tool for a mini PC home server?
Netdata is the easiest to start with — one command installs it, and real-time CPU, memory, disk, and network graphs appear immediately with no configuration. For longer-term metrics and custom dashboards, Prometheus + Grafana is the standard homelab stack. Uptime Kuma is purpose-built for service uptime monitoring with Discord/Telegram alerts.
Does monitoring software use significant resources on a mini PC?
Netdata uses about 100MB of RAM with default settings. Prometheus with node_exporter uses 100-200MB depending on retention period. Grafana uses 200-400MB. The full stack adds 400-700MB total — meaningful on an 8GB machine but comfortable on a 16GB system like the EQ14. Netdata alone is the lightest option for resource-constrained setups.
How do I get alerts when a service goes down on my mini PC?
Uptime Kuma sends push notifications, Discord messages, Telegram alerts, and email when a monitored service becomes unreachable. Install Uptime Kuma via Docker, add each service URL or port as a monitor, and configure your notification channel. It checks every 60 seconds by default and alerts within minutes of a failure.
Can I monitor Docker containers with Grafana?
Yes. Add cAdvisor to your Docker Compose stack — it exposes per-container CPU, memory, and network metrics to Prometheus. Grafana then displays those metrics in dashboards with per-container graphs. The community dashboard ID 893 cAdvisor imports directly into Grafana with one click.
