{"id":1072,"date":"2026-09-07T23:13:59","date_gmt":"2026-09-07T23:13:59","guid":{"rendered":"https:\/\/virtualserversvps.com\/blog\/?p=1072"},"modified":"2026-09-28T22:02:33","modified_gmt":"2026-09-28T22:02:33","slug":"node-exporter-cardinality-control-prometheus-vps","status":"publish","type":"post","link":"https:\/\/virtualserversvps.com\/blog\/node-exporter-cardinality-control-prometheus-vps\/","title":{"rendered":"Node Exporter Cardinality Control: Cutting Prometheus Series Churn on a Single-VPS Stack"},"content":{"rendered":"\n<p class=\"wp-block-paragraph\">A default <code>node_exporter<\/code> on a modern Linux host exposes well over a thousand series, and the collector set keeps growing. On a single VPS running Prometheus locally, the first real constraint is not ingest rate but the size of the active series set held in memory combined with the head block written every two hours. Reduce cardinality with intent and you can keep a 30-day retention window on a 1 GB instance without losing the signals you actually alert on.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Measure cardinality before removing anything<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Start with the top series producers on the <code>\/metrics<\/code> endpoint itself, before Prometheus is even involved. The <code>--collector.disable-defaults<\/code> flag combined with explicit enables is the controlled way to build a minimal set, but first quantify what each collector costs:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>curl -s localhost:9100\/metrics | grep -v '^#' | grep -v '_total$' \\\n  | sed 's\/{.*\/\/; s\/ [0-9].*\/\/' | sort | uniq -c | sort -rn | head -25\n\n# and per labelled metric, worst offenders\necho 'topk(15, count by (__name__)({job=\"node\"}))' \\\n  | curl -s --data-urlencode 'query=topk(15, count by (__name__)({job=\"node\"}))' \\\n      http:\/\/localhost:9090\/api\/v1\/query | python3 -m json.tool | head -60<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">The usual results: <code>node_schedstat_*<\/code> (one series per CPU per state), <code>node_softnet_*<\/code>, <code>node_cpu_seconds_total<\/code> at 8-11 series per core, and <code>node_systemd_unit_state<\/code>, which produces 8 series per systemd unit and can be the single largest contributor on a host with dozens of units. On a 4-vCPU box, <code>node_cpu_seconds_total<\/code> alone is roughly 44 series; add the per-core schedstat breakdown and you have doubled that for data you will never graph.<\/p>\n\n\n\n<figure class=\"wp-block-table\"><table><thead><tr><th>Collector<\/th><th>Series scale<\/th><th>Keep?<\/th><th>Rationale<\/th><\/tr><\/thead><tbody><tr><td>cpu, memory, filesystem<\/td><td>low, per-core\/mount<\/td><td>yes<\/td><td>core alerting input<\/td><\/tr><tr><td>systemd<\/td><td>8 x units<\/td><td>filtered<\/td><td>whitelist units you own<\/td><\/tr><tr><td>schedstat<\/td><td>~30 x cores<\/td><td>no<\/td><td>rarely actionable<\/td><\/tr><tr><td>softnet<\/td><td>~11 x cores<\/td><td>conditional<\/td><td>only for NIC-drop debugging<\/td><\/tr><tr><td>pressure (PSI)<\/td><td>3 resource x 3 windows<\/td><td>yes<\/td><td>best saturation signal available<\/td><\/tr><tr><td>mountstats<\/td><td>large on NFS<\/td><td>no unless NFS<\/td><td>combinatorial explosion<\/td><\/tr><\/tbody><\/table><\/figure>\n\n\n\n<h2 class=\"wp-block-heading\">Filter labels at scrape time, not in queries<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Metric relabeling is cheaper than a query-time filter because the series never enters the TSDB. The distinction matters: a <code>metric_relabel_configs<\/code> drop rule reduces storage and memory, while a PromQL filter only reduces response size. Here is a targeted drop that keeps CPU accounting intact while removing the schedstat and softnet explosion:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>scrape_configs:\n  - job_name: node\n    static_configs:\n      - targets: ['localhost:9100']\n    metric_relabel_configs:\n      - source_labels: [__name__]\n        regex: 'node_schedstat_.*|node_softnet_.*|node_mountstats_.*'\n        action: drop\n      - source_labels: [__name__, unit]\n        regex: 'node_systemd_unit_state;.*\\.(mount|socket|slice|target|automount)'\n        action: drop\n      - source_labels: [__name__]\n        regex: 'node_cpu_seconds_total'\n        action: keep\n      - source_labels: [__name__, mode]\n        regex: 'node_cpu_seconds_total;(guest|guest_nice|steal|iowait)'\n        action: drop<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Dropping <code>iowait<\/code> from <code>node_cpu_seconds_total<\/code> is a judgment call. On a VPS, <code>iowait<\/code> is frequently an artifact of the hypervisor&#8217;s block layer and <code>steal<\/code> is the more reliable contention indicator &#8211; but if you alert on storage saturation via iowait, keep it and drop <code>guest_nice<\/code> instead. The point is that the decision is explicit rather than inherited from the default collector set.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Per-mount and per-device labels<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\"><code>node_filesystem_*<\/code> emits roughly 12 series per mount point across eight metric families. Containerized hosts with overlay and tmpfs mounts included by default can triple this. Filtering on the mountpoint label rather than dropping the whole family preserves the signal you need for the root and data volumes:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>      - source_labels: [mountpoint]\n        regex: '\/run\/.*|\/sys\/.*|\/var\/lib\/docker\/.*|\/dev\/shm'\n        action: drop\n      - source_labels: [fstype]\n        regex: 'tmpfs|overlay|squashfs|nsfs'\n        action: drop\n\n# verify post-change series count\ncurl -s 'http:\/\/localhost:9090\/api\/v1\/query?query=count(count+by(__name__)({job=\"node\"}))' \\\n  | python3 -c 'import sys,json; d=json.load(sys.stdin); print(d[\"data\"][\"result\"][0][\"value\"][1],\"series names\")'<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Check the impact on the head block rather than trusting a series count alone. Prometheus exposes <code>prometheus_tsdb_head_series<\/code> and <code>prometheus_tsdb_head_chunks_storage_size_bytes<\/code>; the second number divided by the first tells you the average chunk footprint and whether a small number of high-churn series is inflating the head.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Retention arithmetic on a small disk<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">A typical compressed sample is 1 to 2 bytes. With 2,000 active series at a 15-second interval, that is 11.5 samples per series per minute, or roughly 33 MB per day at a 1.5-byte average &#8211; about 1 GB for 30 days before index overhead. Reduce the scrape interval to 30 seconds for node metrics you alert on at a 5-minute threshold and the same retention fits in half the space with no loss of resolution for anything you act on.<\/p>\n\n\n\n<ul class=\"wp-block-list\"><li><code>--storage.tsdb.retention.time=30d<\/code> and <code>--storage.tsdb.retention.size=800MB<\/code> together, so a cardinality spike degrades retention instead of filling the disk.<\/li><li>Scrape node metrics at 30s, application metrics at 15s only where a dashboard genuinely needs it.<\/li><li>Never enable a collector to inspect one value once; use <code>curl<\/code> against <code>\/metrics<\/code> for that and leave it disabled.<\/li><li>Alert on <code>prometheus_tsdb_head_series<\/code> growth rate to catch a label regression within one scrape cycle.<\/li><\/ul>\n\n\n\n<p class=\"wp-block-paragraph\">If you need to run Prometheus, Grafana, and the exporter stack alongside the workload itself, the practical constraint is resident memory rather than CPU. Our <a href=\"https:\/\/virtualserversvps.com\/\">VPS plans and available RAM allocations<\/a> list the headroom we reserve for observability daemons on each tier, which is the number to size against when the monitoring stack shares a host with the application it watches.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Remote write versus local storage<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">If you keep Prometheus on the same instance as the workload, disk pressure from the TSDB competes with the application&#8217;s own I\/O. Watch <code>prometheus_tsdb_compaction_duration_seconds<\/code> and the node-level <code>node_disk_io_time_seconds_total<\/code> together: compaction spikes that coincide with application latency spikes are the clear signal to either move metrics to remote write or cap the retention size more aggressively. Cardinality control is what makes either option affordable. Provisioning for the combined resident set is covered in the memory specifications we publish on our <a href=\"https:\/\/virtualserversvps.com\/\">VPS hosting specifications page<\/a>.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>A default node_exporter on a modern Linux host exposes well over a thousand series, and the collector set keeps growing. On a single VPS running Prometheus locally, the first real&#8230;<\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"iawp_total_views":1,"footnotes":""},"categories":[1],"tags":[],"class_list":["post-1072","post","type-post","status-publish","format-standard","hentry","category-vps-guides-tutorials"],"yoast_head":"<!-- This site is optimized with the Yoast SEO Premium plugin v26.1 (Yoast SEO v26.1) - https:\/\/yoast.com\/wordpress\/plugins\/seo\/ -->\n<title>Node Exporter Cardinality Control: Cutting Prometheus Series Churn on a Single-VPS Stack - Virtual Servers VPS Blog<\/title>\n<meta name=\"description\" content=\"A default node_exporter on a modern Linux host exposes well over a thousand series, and the collector set keeps growing. On a single VPS running\" \/>\n<meta name=\"robots\" content=\"index, follow, max-snippet:-1, max-image-preview:large, max-video-preview:-1\" \/>\n<link rel=\"canonical\" href=\"https:\/\/virtualserversvps.com\/blog\/node-exporter-cardinality-control-prometheus-vps\/\" \/>\n<meta property=\"og:locale\" content=\"en_US\" \/>\n<meta property=\"og:type\" content=\"article\" \/>\n<meta property=\"og:title\" content=\"Node Exporter Cardinality Control: Cutting Prometheus Series Churn on a Single-VPS Stack\" \/>\n<meta property=\"og:description\" content=\"Node Exporter Cardinality Control: Cutting Prometheus Series Churn on a Single-VPS Stack\" \/>\n<meta property=\"og:url\" content=\"https:\/\/virtualserversvps.com\/blog\/node-exporter-cardinality-control-prometheus-vps\/\" \/>\n<meta property=\"og:site_name\" content=\"Virtual Servers VPS Blog\" \/>\n<meta property=\"article:published_time\" content=\"2026-09-07T23:13:59+00:00\" \/>\n<meta property=\"article:modified_time\" content=\"2026-09-28T22:02:33+00:00\" \/>\n<meta name=\"author\" content=\"Virtual-Servers-Vps-Editor\" \/>\n<meta name=\"twitter:card\" content=\"summary_large_image\" \/>\n<meta name=\"twitter:label1\" content=\"Written by\" \/>\n\t<meta name=\"twitter:data1\" content=\"Virtual-Servers-Vps-Editor\" \/>\n\t<meta name=\"twitter:label2\" content=\"Est. reading time\" \/>\n\t<meta name=\"twitter:data2\" content=\"5 minutes\" \/>\n<script type=\"application\/ld+json\" class=\"yoast-schema-graph\">{\"@context\":\"https:\/\/schema.org\",\"@graph\":[{\"@type\":\"WebPage\",\"@id\":\"https:\/\/virtualserversvps.com\/blog\/node-exporter-cardinality-control-prometheus-vps\/\",\"url\":\"https:\/\/virtualserversvps.com\/blog\/node-exporter-cardinality-control-prometheus-vps\/\",\"name\":\"Node Exporter Cardinality Control: Cutting Prometheus Series Churn on a Single-VPS Stack - Virtual Servers VPS Blog\",\"isPartOf\":{\"@id\":\"https:\/\/virtualserversvps.com\/blog\/#website\"},\"datePublished\":\"2026-09-07T23:13:59+00:00\",\"dateModified\":\"2026-09-28T22:02:33+00:00\",\"author\":{\"@id\":\"https:\/\/virtualserversvps.com\/blog\/#\/schema\/person\/82a299a8284a66ff49f97c74684724a0\"},\"description\":\"A default node_exporter on a modern Linux host exposes well over a thousand series, and the collector set keeps growing. On a single VPS running\",\"breadcrumb\":{\"@id\":\"https:\/\/virtualserversvps.com\/blog\/node-exporter-cardinality-control-prometheus-vps\/#breadcrumb\"},\"inLanguage\":\"en-US\",\"potentialAction\":[{\"@type\":\"ReadAction\",\"target\":[\"https:\/\/virtualserversvps.com\/blog\/node-exporter-cardinality-control-prometheus-vps\/\"]}]},{\"@type\":\"BreadcrumbList\",\"@id\":\"https:\/\/virtualserversvps.com\/blog\/node-exporter-cardinality-control-prometheus-vps\/#breadcrumb\",\"itemListElement\":[{\"@type\":\"ListItem\",\"position\":1,\"name\":\"Home\",\"item\":\"https:\/\/virtualserversvps.com\/blog\/\"},{\"@type\":\"ListItem\",\"position\":2,\"name\":\"Node Exporter Cardinality Control: Cutting Prometheus Series Churn on a Single-VPS Stack\"}]},{\"@type\":\"WebSite\",\"@id\":\"https:\/\/virtualserversvps.com\/blog\/#website\",\"url\":\"https:\/\/virtualserversvps.com\/blog\/\",\"name\":\"Virtual Servers VPS Blog\",\"description\":\"\",\"potentialAction\":[{\"@type\":\"SearchAction\",\"target\":{\"@type\":\"EntryPoint\",\"urlTemplate\":\"https:\/\/virtualserversvps.com\/blog\/?s={search_term_string}\"},\"query-input\":{\"@type\":\"PropertyValueSpecification\",\"valueRequired\":true,\"valueName\":\"search_term_string\"}}],\"inLanguage\":\"en-US\"},{\"@type\":\"Person\",\"@id\":\"https:\/\/virtualserversvps.com\/blog\/#\/schema\/person\/82a299a8284a66ff49f97c74684724a0\",\"name\":\"Virtual-Servers-Vps-Editor\",\"image\":{\"@type\":\"ImageObject\",\"inLanguage\":\"en-US\",\"@id\":\"https:\/\/virtualserversvps.com\/blog\/#\/schema\/person\/image\/\",\"url\":\"https:\/\/secure.gravatar.com\/avatar\/d820b15f1cd028e97610d9adf536df7be5cb6423869967037d468d5355fa003f?s=96&d=mm&r=g\",\"contentUrl\":\"https:\/\/secure.gravatar.com\/avatar\/d820b15f1cd028e97610d9adf536df7be5cb6423869967037d468d5355fa003f?s=96&d=mm&r=g\",\"caption\":\"Virtual-Servers-Vps-Editor\"},\"sameAs\":[\"https:\/\/virtualserversvps.com\/blog\"],\"url\":\"https:\/\/virtualserversvps.com\/blog\/author\/virtualserversvps\/\"}]}<\/script>\n<!-- \/ Yoast SEO Premium plugin. -->","yoast_head_json":{"title":"Node Exporter Cardinality Control: Cutting Prometheus Series Churn on a Single-VPS Stack - Virtual Servers VPS Blog","description":"A default node_exporter on a modern Linux host exposes well over a thousand series, and the collector set keeps growing. On a single VPS running","robots":{"index":"index","follow":"follow","max-snippet":"max-snippet:-1","max-image-preview":"max-image-preview:large","max-video-preview":"max-video-preview:-1"},"canonical":"https:\/\/virtualserversvps.com\/blog\/node-exporter-cardinality-control-prometheus-vps\/","og_locale":"en_US","og_type":"article","og_title":"Node Exporter Cardinality Control: Cutting Prometheus Series Churn on a Single-VPS Stack","og_description":"Node Exporter Cardinality Control: Cutting Prometheus Series Churn on a Single-VPS Stack","og_url":"https:\/\/virtualserversvps.com\/blog\/node-exporter-cardinality-control-prometheus-vps\/","og_site_name":"Virtual Servers VPS Blog","article_published_time":"2026-09-07T23:13:59+00:00","article_modified_time":"2026-09-28T22:02:33+00:00","author":"Virtual-Servers-Vps-Editor","twitter_card":"summary_large_image","twitter_misc":{"Written by":"Virtual-Servers-Vps-Editor","Est. reading time":"5 minutes"},"schema":{"@context":"https:\/\/schema.org","@graph":[{"@type":"WebPage","@id":"https:\/\/virtualserversvps.com\/blog\/node-exporter-cardinality-control-prometheus-vps\/","url":"https:\/\/virtualserversvps.com\/blog\/node-exporter-cardinality-control-prometheus-vps\/","name":"Node Exporter Cardinality Control: Cutting Prometheus Series Churn on a Single-VPS Stack - Virtual Servers VPS Blog","isPartOf":{"@id":"https:\/\/virtualserversvps.com\/blog\/#website"},"datePublished":"2026-09-07T23:13:59+00:00","dateModified":"2026-09-28T22:02:33+00:00","author":{"@id":"https:\/\/virtualserversvps.com\/blog\/#\/schema\/person\/82a299a8284a66ff49f97c74684724a0"},"description":"A default node_exporter on a modern Linux host exposes well over a thousand series, and the collector set keeps growing. On a single VPS running","breadcrumb":{"@id":"https:\/\/virtualserversvps.com\/blog\/node-exporter-cardinality-control-prometheus-vps\/#breadcrumb"},"inLanguage":"en-US","potentialAction":[{"@type":"ReadAction","target":["https:\/\/virtualserversvps.com\/blog\/node-exporter-cardinality-control-prometheus-vps\/"]}]},{"@type":"BreadcrumbList","@id":"https:\/\/virtualserversvps.com\/blog\/node-exporter-cardinality-control-prometheus-vps\/#breadcrumb","itemListElement":[{"@type":"ListItem","position":1,"name":"Home","item":"https:\/\/virtualserversvps.com\/blog\/"},{"@type":"ListItem","position":2,"name":"Node Exporter Cardinality Control: Cutting Prometheus Series Churn on a Single-VPS Stack"}]},{"@type":"WebSite","@id":"https:\/\/virtualserversvps.com\/blog\/#website","url":"https:\/\/virtualserversvps.com\/blog\/","name":"Virtual Servers VPS Blog","description":"","potentialAction":[{"@type":"SearchAction","target":{"@type":"EntryPoint","urlTemplate":"https:\/\/virtualserversvps.com\/blog\/?s={search_term_string}"},"query-input":{"@type":"PropertyValueSpecification","valueRequired":true,"valueName":"search_term_string"}}],"inLanguage":"en-US"},{"@type":"Person","@id":"https:\/\/virtualserversvps.com\/blog\/#\/schema\/person\/82a299a8284a66ff49f97c74684724a0","name":"Virtual-Servers-Vps-Editor","image":{"@type":"ImageObject","inLanguage":"en-US","@id":"https:\/\/virtualserversvps.com\/blog\/#\/schema\/person\/image\/","url":"https:\/\/secure.gravatar.com\/avatar\/d820b15f1cd028e97610d9adf536df7be5cb6423869967037d468d5355fa003f?s=96&d=mm&r=g","contentUrl":"https:\/\/secure.gravatar.com\/avatar\/d820b15f1cd028e97610d9adf536df7be5cb6423869967037d468d5355fa003f?s=96&d=mm&r=g","caption":"Virtual-Servers-Vps-Editor"},"sameAs":["https:\/\/virtualserversvps.com\/blog"],"url":"https:\/\/virtualserversvps.com\/blog\/author\/virtualserversvps\/"}]}},"_links":{"self":[{"href":"https:\/\/virtualserversvps.com\/blog\/wp-json\/wp\/v2\/posts\/1072","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/virtualserversvps.com\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/virtualserversvps.com\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/virtualserversvps.com\/blog\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/virtualserversvps.com\/blog\/wp-json\/wp\/v2\/comments?post=1072"}],"version-history":[{"count":3,"href":"https:\/\/virtualserversvps.com\/blog\/wp-json\/wp\/v2\/posts\/1072\/revisions"}],"predecessor-version":[{"id":1244,"href":"https:\/\/virtualserversvps.com\/blog\/wp-json\/wp\/v2\/posts\/1072\/revisions\/1244"}],"wp:attachment":[{"href":"https:\/\/virtualserversvps.com\/blog\/wp-json\/wp\/v2\/media?parent=1072"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/virtualserversvps.com\/blog\/wp-json\/wp\/v2\/categories?post=1072"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/virtualserversvps.com\/blog\/wp-json\/wp\/v2\/tags?post=1072"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}