{"id":1541,"date":"2026-10-29T23:21:09","date_gmt":"2026-10-29T23:21:09","guid":{"rendered":"https:\/\/virtualserversvps.com\/blog\/?p=1541"},"modified":"2026-10-29T23:21:09","modified_gmt":"2026-10-29T23:21:09","slug":"measuring-vps-cpu-steal-time-accurately","status":"publish","type":"post","link":"https:\/\/virtualserversvps.com\/blog\/measuring-vps-cpu-steal-time-accurately\/","title":{"rendered":"Measuring CPU Steal Time Accurately on a KVM VPS"},"content":{"rendered":"<p class=\"wp-block-paragraph\">Steal time is the one CPU metric on a VPS that describes something you cannot fix from inside the guest. It counts the cycles during which your vCPU was runnable but the hypervisor scheduled something else on the physical core. Almost every guide stops at &#8220;look at the %st column in <code>top<\/code>&#8220;, which is where the measurement usually goes wrong: the number depends on the sampling interval, on which CPU you read, and on whether the guest kernel can even see the delay.<\/p>\n\n\n<h2 class=\"wp-block-heading\">The Field Semantics in \/proc\/stat<\/h2>\n\n\n<p class=\"wp-block-paragraph\">Each line in <code>\/proc\/stat<\/code> beginning with <code>cpu<\/code> is a cumulative tick counter. The first line, <code>cpu<\/code>, is the aggregate across all vCPUs; <code>cpu0<\/code>, <code>cpu1<\/code> and so on are per-vCPU. The fields are <code>user nice system idle iowait irq softirq steal guest guest_nice<\/code>. There is no field for hypervisor overhead you did not observe \u2014 steal is only what the guest kernel was told, via paravirtualised clocks, while it had a runnable task waiting.<\/p>\n\n<p class=\"wp-block-paragraph\">The unit is USER_HZ ticks, almost always 1\/100 s on x86. Do not treat the values as percentages: they are counters, and the only meaningful number is a difference over a known interval. <code>irq<\/code> and <code>softirq<\/code> are accounted separately, and on a modern kernel <code>guest<\/code> time is already included in <code>user<\/code>, so subtracting fields naively double-counts. The invariant to watch is that the ten fields sum to the elapsed interval in ticks.<\/p>\n\n\n<h2 class=\"wp-block-heading\">Reading It Correctly: Aggregate vs Per-vCPU<\/h2>\n\n\n<p class=\"wp-block-paragraph\">The aggregate <code>cpu<\/code> line is an average across vCPUs, and averaging destroys the signal you care about. A single pegged vCPU with heavy steal, next to three idle vCPUs, shows up as a few percent on the aggregate and is invisible. Always inspect per-CPU lines when diagnosing latency.<\/p>\n\n\n<pre class=\"wp-block-code\"><code>awk '\/^cpu[0-9]\/{printf &quot;%s steal=%s user=%s sys=%s idle=%s\\n&quot;, $1,$9,$2,$4,$5}' \/proc\/stat\nmpstat -P ALL 5 3          # per-CPU, 5 s interval, 3 samples\npidstat -u 1 -t            # which threads are consuming the CPU<\/code><\/pre>\n\n\n<p class=\"wp-block-paragraph\"><code>mpstat<\/code> takes a delta internally and is far less error-prone than eyeballing <code>top<\/code>. Its <code>%steal<\/code> column is computed the same way you should compute yours by hand. If <code>%steal<\/code> is consistently above a few percent on a vCPU while your own <code>%user<\/code> and <code>%sys<\/code> on that core are low, the core is waiting on the host. If <code>%steal<\/code> is high and <code>%user<\/code> is also high, you are simply busy and the host is still managing to run you \u2014 that is normal framing.<\/p>\n\n\n<h2 class=\"wp-block-heading\">Sampling Interval Pitfalls<\/h2>\n\n\n<p class=\"wp-block-paragraph\">Short intervals produce unusable numbers. At a 1-second interval with 100 Hz ticks, each vCPU&#8217;s steal counter can only move in units of 0-100 ticks, and a single scheduling delay of 20 ms shows up as 2 percent on one sample and 0 percent on the next. Averaging across 1-second samples systematically understates bursty contention and overstates smooth contention.<\/p>\n\n<p class=\"wp-block-paragraph\">Use an interval of at least 10 seconds, and take several samples rather than one. Also be aware of the reverse error: read too far apart and you measure an average including periods when the host was quiet. For latency-sensitive diagnosis, correlate steal with your own request latency on the same timebase rather than summarising both separately.<\/p>\n\n\n<pre class=\"wp-block-code\"><code># 10-second windows, 30 samples, per-vCPU steal in percent\nfor i in $(seq 30); do\n  awk '\/^cpu[0-9]\/{s[$1]=$9; t[$1]=$2+$3+$4+$5+$6+$7+$8+$9}\n       END{for(c in s) printf &quot;%s %s\\n&quot;, c, s[c]}' \/proc\/stat &gt; \/tmp\/a\n  sleep 10\n  awk '\/^cpu[0-9]\/{s[$1]=$9; t[$1]=$2+$3+$4+$5+$6+$7+$8+$9}\n       END{for(c in s) printf &quot;%s %s\\n&quot;, c, s[c]}' \/proc\/stat &gt; \/tmp\/b\n  paste \/tmp\/a \/tmp\/b | awk '{d=$4-$2; printf &quot;%s %.1f%%\\n&quot;, $1, d*100\/(10*100)}'\ndone<\/code><\/pre>\n\n\n<p class=\"wp-block-paragraph\">Adjust the divisor to your actual <code>getconf CLK_TCK<\/code> and interval. The script above assumes 100 Hz and a 10-second sleep; both are correct on stock x86_64 images but worth confirming on unusual kernels or architectures.<\/p>\n\n\n<h2 class=\"wp-block-heading\">What Steal Does Not Tell You<\/h2>\n\n\n<p class=\"wp-block-paragraph\">Steal accounts for time the guest knew it was runnable and not running. It does not capture cache and memory-bandwidth interference from co-tenants: another VM thrashing the last-level cache makes your instructions slower while your vCPU is scheduled the whole time. That shows up as elevated cycles-per-instruction and lower throughput at unchanged <code>%user<\/code>, not as steal.<\/p>\n\n<p class=\"wp-block-paragraph\">It also does not capture I\/O time spent waiting on a shared storage backend, which lands in <code>%iowait<\/code> and, on a VPS, often reflects a queue on the host rather than your own disk. When steal looks clean but latency is bad, check <code>%iowait<\/code>, the block-layer queue depths in <code>\/sys\/block\/*\/stat<\/code>, and the per-process delay accounting in <code>\/proc\/&lt;pid&gt;\/stat<\/code>.<\/p>\n\n\n<pre class=\"wp-block-code\"><code>grep -E '^(cpu|ctxt|procs_running)' \/proc\/stat\ncat \/proc\/pressure\/cpu          # PSI: some\/full avg10\/avg60\/avg300\niostat -xz 5 3                  # await and aqu-sz per device<\/code><\/pre>\n\n\n<p class=\"wp-block-paragraph\">Pressure stall information is the best complement to steal: <code>full<\/code> in <code>\/proc\/pressure\/cpu<\/code> means every runnable task was stalled, which is what a latency-critical service actually experiences. Steal explains part of that stall; PSI measures the consequence.<\/p>\n\n\n<h2 class=\"wp-block-heading\">Correlating Steal With Latency<\/h2>\n\n\n<p class=\"wp-block-paragraph\">The decisive test is whether latency degrades when steal rises. Log both on the same wall clock. Sample steal every 10 seconds into a file, and have your application log response times with timestamps; then join the two by minute and look at the correlation. If p99 latency doubles in the minutes when steal is elevated and returns when it falls, the mechanism is established.<\/p>\n\n\n<pre class=\"wp-block-code\"><code># minimal logger: epoch, per-CPU steal ticks\nwhile :; do\n  awk -v t=$(date +%s) '\/^cpu[0-9]\/{print t, $1, $9}' \/proc\/stat\n  sleep 10\ndone &gt;&gt; \/tmp\/steal.log\n\n# and on the app side, log $request_time or equivalent per request,\n# then bucket both by minute and compare medians<\/code><\/pre>\n\n\n<p class=\"wp-block-paragraph\">An important control: run the same correlation on a quiet period and on a busy period. A correlation that holds only during your own peak hours may simply be your workload, not the host. Comparing your peak against the host&#8217;s behaviour at a time when your load is flat is the cleanest way to separate the two.<\/p>\n\n\n<h2 class=\"wp-block-heading\">Deciding Whether You Are Being Throttled<\/h2>\n\n\n<p class=\"wp-block-paragraph\">There are two distinct mechanisms and they look similar from <code>top<\/code>. CPU quota throttling \u2014 from a cgroup CPU limit set by the provider \u2014 appears as the vCPU being forcibly descheduled once its budget is spent inside each period. You can read it directly in the guest if cgroup v2 statistics are exposed: <code>cpu.stat<\/code> reports <code>nr_throttled<\/code> and <code>throttled_usec<\/code>.<\/p>\n\n\n<pre class=\"wp-block-code\"><code>cat \/sys\/fs\/cgroup\/cpu.stat\ncat \/sys\/fs\/cgroup\/cpu.max\n# per-process cgroup if your service runs in a slice\ncat \/sys\/fs\/cgroup\/system.slice\/&lt;unit&gt;.slice\/cpu.stat<\/code><\/pre>\n\n\n<p class=\"wp-block-paragraph\">If <code>throttled_usec<\/code> climbs steadily and <code>cpu.max<\/code> shows a finite quota, you are being throttled by your own plan&#8217;s limit rather than by co-tenant contention, and steal may remain near zero. The two diagnoses lead to different actions: throttle means the plan&#8217;s CPU allowance is too small for the workload, while steal means the host is oversubscribed. Sampling both counters for a few minutes distinguishes them reliably.<\/p>\n\n\n<h2 class=\"wp-block-heading\">Reporting the Finding to a Provider<\/h2>\n\n\n<p class=\"wp-block-paragraph\">If you conclude the host is oversubscribed, a report with numbers gets a better response than &#8220;the server is slow&#8221;. Include the per-vCPU steal figures over a defined interval, the corresponding PSI <code>full<\/code> values, the timestamps in UTC, and the measured latency degradation. That evidence is reproducible and hard to dismiss, and it also tells you whether the issue is transient or structural.<\/p>\n\n<p class=\"wp-block-paragraph\">If it is structural and the workload is production, the fix is a plan with dedicated or guaranteed CPU rather than further measurement \u2014 the trade-offs between shared and dedicated allocations are covered in <a href=\"https:\/\/virtualserversvps.com\/\">our comparison of VPS plans and their CPU guarantees<\/a>. Before moving, it is worth confirming the guest can see steal at all: some hypervisor configurations report it unreliably, which is one of several signals that the instance is not a good fit, discussed alongside <a href=\"https:\/\/virtualserversvps.com\/blog\/free-vps-options-limits-benchmarks-upgrade-points\">the practical limits of free and low-cost VPS tiers<\/a>.<\/p>\n\n\n<h2 class=\"wp-block-heading\">A Repeatable Measurement Checklist<\/h2>\n\n\n\n<ul class=\"wp-block-list\"><li>Read per-vCPU <code>steal<\/code> from <code>\/proc\/stat<\/code>, never the aggregate line alone.<\/li><li>Use 10-second or longer intervals and take at least 30 samples.<\/li><li>Compute percentages from counter deltas, not from single readings.<\/li><li>Accompany steal with <code>\/proc\/pressure\/cpu<\/code> and <code>%iowait<\/code> to see the consequence.<\/li><li>Check cgroup <code>cpu.stat<\/code> for throttling before blaming the host.<\/li><li>Correlate against application p99 latency on the same timebase.<\/li><\/ul>\n\n\n<p class=\"wp-block-paragraph\">Accurate steal measurement is mostly about refusing to trust a single number from a one-second sample. Read the counters as deltas per vCPU, pair them with a stall metric, and separate quota throttling from host contention before deciding whether to tune or to change plans.<\/p>","protected":false},"excerpt":{"rendered":"<p>Steal time is the one CPU metric on a VPS that describes something you cannot fix from inside the guest. It counts the cycles during which your vCPU was runnable&#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-1541","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>Measuring CPU Steal Time Accurately on a KVM VPS - Virtual Servers VPS Blog<\/title>\n<meta name=\"description\" content=\"Steal time is the one CPU metric on a VPS that describes something you cannot fix from inside the guest. It counts the cycles during which your vCPU was\" \/>\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\/measuring-vps-cpu-steal-time-accurately\/\" \/>\n<meta property=\"og:locale\" content=\"en_US\" \/>\n<meta property=\"og:type\" content=\"article\" \/>\n<meta property=\"og:title\" content=\"Measuring CPU Steal Time Accurately on a KVM VPS\" \/>\n<meta property=\"og:description\" content=\"Measuring CPU Steal Time Accurately on a KVM VPS\" \/>\n<meta property=\"og:url\" content=\"https:\/\/virtualserversvps.com\/blog\/measuring-vps-cpu-steal-time-accurately\/\" \/>\n<meta property=\"og:site_name\" content=\"Virtual Servers VPS Blog\" \/>\n<meta property=\"article:published_time\" content=\"2026-10-29T23:21:09+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=\"7 minutes\" \/>\n<script type=\"application\/ld+json\" class=\"yoast-schema-graph\">{\"@context\":\"https:\/\/schema.org\",\"@graph\":[{\"@type\":\"WebPage\",\"@id\":\"https:\/\/virtualserversvps.com\/blog\/measuring-vps-cpu-steal-time-accurately\/\",\"url\":\"https:\/\/virtualserversvps.com\/blog\/measuring-vps-cpu-steal-time-accurately\/\",\"name\":\"Measuring CPU Steal Time Accurately on a KVM VPS - Virtual Servers VPS Blog\",\"isPartOf\":{\"@id\":\"https:\/\/virtualserversvps.com\/blog\/#website\"},\"datePublished\":\"2026-10-29T23:21:09+00:00\",\"author\":{\"@id\":\"https:\/\/virtualserversvps.com\/blog\/#\/schema\/person\/82a299a8284a66ff49f97c74684724a0\"},\"description\":\"Steal time is the one CPU metric on a VPS that describes something you cannot fix from inside the guest. It counts the cycles during which your vCPU was\",\"breadcrumb\":{\"@id\":\"https:\/\/virtualserversvps.com\/blog\/measuring-vps-cpu-steal-time-accurately\/#breadcrumb\"},\"inLanguage\":\"en-US\",\"potentialAction\":[{\"@type\":\"ReadAction\",\"target\":[\"https:\/\/virtualserversvps.com\/blog\/measuring-vps-cpu-steal-time-accurately\/\"]}]},{\"@type\":\"BreadcrumbList\",\"@id\":\"https:\/\/virtualserversvps.com\/blog\/measuring-vps-cpu-steal-time-accurately\/#breadcrumb\",\"itemListElement\":[{\"@type\":\"ListItem\",\"position\":1,\"name\":\"Home\",\"item\":\"https:\/\/virtualserversvps.com\/blog\/\"},{\"@type\":\"ListItem\",\"position\":2,\"name\":\"Measuring CPU Steal Time Accurately on a KVM VPS\"}]},{\"@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":"Measuring CPU Steal Time Accurately on a KVM VPS - Virtual Servers VPS Blog","description":"Steal time is the one CPU metric on a VPS that describes something you cannot fix from inside the guest. It counts the cycles during which your vCPU was","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\/measuring-vps-cpu-steal-time-accurately\/","og_locale":"en_US","og_type":"article","og_title":"Measuring CPU Steal Time Accurately on a KVM VPS","og_description":"Measuring CPU Steal Time Accurately on a KVM VPS","og_url":"https:\/\/virtualserversvps.com\/blog\/measuring-vps-cpu-steal-time-accurately\/","og_site_name":"Virtual Servers VPS Blog","article_published_time":"2026-10-29T23:21:09+00:00","author":"Virtual-Servers-Vps-Editor","twitter_card":"summary_large_image","twitter_misc":{"Written by":"Virtual-Servers-Vps-Editor","Est. reading time":"7 minutes"},"schema":{"@context":"https:\/\/schema.org","@graph":[{"@type":"WebPage","@id":"https:\/\/virtualserversvps.com\/blog\/measuring-vps-cpu-steal-time-accurately\/","url":"https:\/\/virtualserversvps.com\/blog\/measuring-vps-cpu-steal-time-accurately\/","name":"Measuring CPU Steal Time Accurately on a KVM VPS - Virtual Servers VPS Blog","isPartOf":{"@id":"https:\/\/virtualserversvps.com\/blog\/#website"},"datePublished":"2026-10-29T23:21:09+00:00","author":{"@id":"https:\/\/virtualserversvps.com\/blog\/#\/schema\/person\/82a299a8284a66ff49f97c74684724a0"},"description":"Steal time is the one CPU metric on a VPS that describes something you cannot fix from inside the guest. It counts the cycles during which your vCPU was","breadcrumb":{"@id":"https:\/\/virtualserversvps.com\/blog\/measuring-vps-cpu-steal-time-accurately\/#breadcrumb"},"inLanguage":"en-US","potentialAction":[{"@type":"ReadAction","target":["https:\/\/virtualserversvps.com\/blog\/measuring-vps-cpu-steal-time-accurately\/"]}]},{"@type":"BreadcrumbList","@id":"https:\/\/virtualserversvps.com\/blog\/measuring-vps-cpu-steal-time-accurately\/#breadcrumb","itemListElement":[{"@type":"ListItem","position":1,"name":"Home","item":"https:\/\/virtualserversvps.com\/blog\/"},{"@type":"ListItem","position":2,"name":"Measuring CPU Steal Time Accurately on a KVM VPS"}]},{"@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\/1541","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=1541"}],"version-history":[{"count":1,"href":"https:\/\/virtualserversvps.com\/blog\/wp-json\/wp\/v2\/posts\/1541\/revisions"}],"predecessor-version":[{"id":1544,"href":"https:\/\/virtualserversvps.com\/blog\/wp-json\/wp\/v2\/posts\/1541\/revisions\/1544"}],"wp:attachment":[{"href":"https:\/\/virtualserversvps.com\/blog\/wp-json\/wp\/v2\/media?parent=1541"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/virtualserversvps.com\/blog\/wp-json\/wp\/v2\/categories?post=1541"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/virtualserversvps.com\/blog\/wp-json\/wp\/v2\/tags?post=1541"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}