Calculating CPU Headroom

Introduction: From Status Indicators to Transformed Time-Series

In the previous lesson, you learned how to use aggregate functions like AVG() to display a single summary value in a Stat Panel. That was all about creating status indicators — panels that answer "What's the overall situation right now?" at a glance.

Now we're going to explore a different visualization need: showing transformed metrics over time. Instead of aggregating many rows into one number, you'll transform each data point into something more meaningful before Grafana plots it. Think of it this way: aggregate functions help you create status panels (one big number), while calculations help you create insightful time-series graphs (lines that tell a story).

The Problem: Raw Metrics vs. Operational Insights

Here's the visualization problem we're solving. Imagine you have a time-series panel showing "CPU Usage" hovering around 75%. That's useful information, but when stakeholders look at your graphs during a planning meeting, they're asking: "Can we handle more load?" or "Do we have room to deploy this new feature?" A graph showing "CPU Headroom" at 25% answers that question directly. Same underlying data, but visualized in a way that matches how people think about operational capacity.

This concept of CPU headroom — the percentage of CPU capacity still available — is what we call a derived metric. It doesn't exist in your database tables, but you can calculate it in your query so Grafana displays it as if it were a real column. By the end of this lesson, you'll know how to create time-series panels that show derived metrics and how to name them properly so your panel legends are clear and professional.

Creating Derived Metrics for Time-Series Panels

Let's talk about what Grafana needs from your query to plot a proper time-series graph. You already know the basics: a timestamp column, a metric name for the legend, and values to plot. What you haven't done yet is transform those values before Grafana sees them. That's what derived metrics are all about — performing calculations in your query so Grafana receives exactly what you want to visualize.

When you're building a time-series panel in Grafana, you're not just pulling data — you're deciding what story you want the graph to tell. If you query usage directly from metrics_cpu, your graph shows a line representing consumption. But if you calculate 100 - usage in your query, Grafana receives headroom values instead, and your graph shows a line representing available capacity. The visualization completely changes the message.

Here's what that looks like in practice. In your query editor, you write:

SQL
SELECT
  $__time(ts),
  'CPU Headroom %' AS metric,
  100 - usage AS value
FROM public.metrics_cpu
WHERE $__timeFilter(ts)
ORDER BY 1;

When Grafana executes this query, it receives three columns: formatted timestamps, a metric name, and calculated values. It doesn't know or care that you performed arithmetic — it just sees a column named value and plots those numbers over time. This is the beauty of derived metrics: you control exactly what Grafana visualizes by transforming data in your query.

Notice how the calculation 100 - usage happens for each row individually. If your database has measurements every 10 seconds over a 6-hour period, that's about 2,160 data points. Grafana will plot all 2,160 calculated headroom values as a continuous line on your graph. The transformation happens row-by-row, preserving your time-series structure so you get a proper line graph showing how headroom changes over time.

This is fundamentally different from the aggregate functions you used in Stat Panels. Those collapsed many rows into one number for a status indicator. Derived metrics transform each row individually for time-series visualizations. Both techniques are essential in Grafana — aggregates for current status, calculations for trends and patterns.

Sign up

Join the 1M+ learners on CodeSignal

Be a part of our community of 1M+ users who develop and demonstrate their skills on CodeSignal