Grafana Dashboards That Answer CPU, Disk, and Memory
Build a small Grafana dashboard whose panels answer CPU steal, disk await, and memory pressure with PromQL you can defend during an incident.
Netbay Infrastructure Team
Netbay Engineering
On this page
A Grafana folder full of imported JSON is not a dashboard. A dashboard is a set of panels that answer the questions you actually ask at 2 a.m.: is the CPU busy doing work or waiting, is memory about to OOM, and is the disk the thing that got slow. On a single VPS those three questions cover most incidents. Everything else is decoration until those three are honest.
This post assumes Prometheus already scrapes node_exporter on localhost. Grafana is the browser. Install it as a package or a binary, bind it to localhost, and tunnel. Do not publish Grafana to the public internet with admin/admin. DDoS Protected L3/L4 filtering will not save a default password.
CPU panels that separate work from wait
The first CPU panel is not a single utilization line. Stack the rate of node_cpu_seconds_total by mode: user, system, iowait, steal, irq, softirq, idle. Idle should dominate on a quiet Xeon Platinum vCPU. User should rise with the app. Iowait rising while user stays flat means the process is stuck on storage. Steal rising means the hypervisor is not giving you the core you paid for.
The second CPU panel is load versus core count. node_load1 divided by count of cpu modes at idle is the cheap saturation ratio. When that ratio sits above 1.0 and iowait is the growing slice, you do not have a compute problem. When that ratio sits above 1.0 and user plus system own the pie, you need more CPU or less work.
groups:
- name: cpu-panels
rules:
- record: instance:cpu_busy:ratio
expr: 1 - avg without (cpu, mode) (rate(node_cpu_seconds_total{mode="idle"}[5m]))
- record: instance:cpu_iowait:ratio
expr: avg without (cpu, mode) (rate(node_cpu_seconds_total{mode="iowait"}[5m]))
- record: instance:cpu_steal:ratio
expr: avg without (cpu, mode) (rate(node_cpu_seconds_total{mode="steal"}[5m]))Recording rules are optional on one node, but they keep panel queries short. In Grafana, graph those three ratios on one timeline with a 0 to 1 Y axis. The moment steal leaves the noise floor, you have a virtualization story, not an application story.
Memory panels that use MemAvailable
Never graph node_memory_MemFree_bytes as "free memory." Graph MemAvailable against MemTotal as a percent. Add a second panel for Cached plus Buffers so you can see cache give back under pressure. Add a third for SwapTotal minus SwapFree. If available percent slides while cache stays huge, the kernel is doing its job. If available percent slides and swap used climbs, you are thrashing.
RSS of the top process belongs next to the host chart. node_exporter does not know your app name unless you run a process collector or a separate exporter. Until then, keep a simple process panel from node_processes or accept that you will still SSH and run ps during incidents. The dashboard's job is to tell you the host is in trouble; ps tells you who.
curl -sG http://127.0.0.1:9090/api/v1/query --data-urlencode 'query=100 * (node_memory_MemAvailable_bytes / node_memory_MemTotal_bytes)'
curl -sG http://127.0.0.1:9090/api/v1/query --data-urlencode 'query=node_memory_SwapTotal_bytes - node_memory_SwapFree_bytes'Paste the same expressions into Grafana as gauge plus time series. Threshold the gauge at 20 percent available as warning and 10 percent as danger. Those numbers are starting points from your CLI baseline, not universal laws. A JVM that sizes the heap to 70 percent of RAM will sit near the warning line forever and that can be fine.
Disk panels that show fill and latency
Two disk stories kill a VPS: the filesystem fills, and the filesystem gets slow. For fill, graph 1 minus avail over size for the root mount, and graph inode avail over inode files. For latency, graph rate of reads and writes plus node_disk_io_time_weighted_seconds as await. On High-Speed SSD, await should stay in low milliseconds. A backup job will spike it; a stuck fsync will hold it.
Exclude tmpfs, overlay, and loop mounts or the panel turns into noise. node_exporter already labels mountpoint. Filter to mountpoint="/" and the data disk if you split them. If you did not split them, root is the only panel that matters, and log growth is a disk alert waiting to happen.
Put disk used percent, inode used percent, and await on one row. The row answers: are we full, are we out of files, or are we waiting. That is the entire disk incident taxonomy for a single node.
Layout: one screen, no decoration
Top row: CPU stacked modes, load ratio, steal. Middle row: MemAvailable percent, swap used, top process note. Bottom row: root used percent, inodes, disk await. Time picker default 6 hours, not 7 days. Refresh 30 seconds. No pie charts. No world maps. No unused Node Exporter Full import with 40 rows you will never scroll.
Name panels as questions: CPU waiting on disk, Memory left for processes, Root filesystem hours to full. Future you at 2 a.m. reads questions faster than metric names. Save the dashboard in Git as JSON so a rebuilt VPS does not start from a blank folder.
Takeaway
Three honest rows beat a gallery of imported panels. Graph CPU modes including steal, MemAvailable not MemFree, and disk fill plus await. Build that dashboard on a Netbay Ubuntu VPS in Lucknow and keep Grafana off the public NIC — netbayhosts.in.
Keep reading
Follow along on a real VPS
Deploy Linux in under 60 seconds
These guides are written against Ubuntu, Debian, and RHEL-family images — the same ones on NetBay.
Deploy an instance