Skip to main content
    VPS

    CPU Steal Time on a VPS: How to Measure Oversubscription

    September 13, 2026
    19 min read
    CPU Steal Time on a VPS: How to Measure Oversubscription

    CPU steal time is the share of time your virtual machine was ready to run and did not get a physical CPU, because the host was busy running something else. You read it in the st column of top, vmstat or mpstat. Sustained under 1 percent is healthy. One to five percent is ordinary on a shared vCPU node. Above ten percent for hours at a time means the physical machine is oversold and you are paying for CPU you are not receiving.

    That is the short answer. Here is the longer one, from a company that sells VPS hosting and therefore has an obvious incentive not to write it: every provider selling shared vCPU oversubscribes to some degree. That is what makes a 3.49 dollar server possible at all. The useful question was never whether your host oversubscribes. It is by how much, and whether you can measure it. Steal time is the measurement, and it works on our machines exactly as well as it works on anyone else's.

    Key Takeaways

    • Steal time counts only involuntary wait. Idle time is not stolen time, so a number above zero always means your workload actually wanted the CPU and was made to queue.
    • Four ways to read it: the st field in top, the st column in vmstat 1 30, %steal in mpstat -P ALL, and the eighth counter on the cpu line of /proc/stat if you want to script it.
    • The percentage bands everyone quotes are rules of thumb, not standards. No kernel document, RFC or vendor SLA defines a "good" steal time.
    • A thirty-second sample proves nothing. Oversubscription is worst at peak, so log for a full 24 hours and look at the distribution, not the average.
    • Near-zero steal time does not mean your VPS is fast. Disk latency, single-thread clock speed, network round trips and PHP-FPM worker exhaustion all cause slowness that steal time cannot see.
    • Dedicated vCPU is worth the premium for sustained CPU work: CI runners, video encoding, game servers, busy database primaries. For most web hosting it is money spent on headroom you will never use.

    What CPU Steal Time Actually Is

    Your VPS is not a computer. It is a process on somebody else's computer. Each of your virtual CPUs is a thread on the physical host, and the host's scheduler decides when that thread runs, in the same way your own kernel decides when nginx runs versus MySQL.

    When your vCPU thread is runnable but the host scheduler has not put it on a physical core, that waiting is steal time. The Linux kernel's own documentation for /proc/stat describes the field in two words: involuntary wait. That phrasing is precise and worth holding onto. Your VM did not choose to wait. It had work queued and the hardware was busy elsewhere.

    What the hypervisor is doing while steal accrues

    On KVM, which is what most modern VPS products use, this is not a guess the guest makes. It is a number the hypervisor hands over. The guest kernel registers a shared memory structure with the host through a paravirtual MSR, documented by the kernel project as MSR_KVM_STEAL_TIME, and the host writes into it. The kernel's KVM MSR documentation defines the field as "the amount of time in which this vCPU did not run, in nanoseconds", and adds the crucial qualifier: "Time during which the vcpu is idle, will not be reported as steal time."

    Two consequences follow from that, and most articles on this topic miss both.

    First, steal time is not inflated by your own idleness. A server doing nothing all day reports zero steal, no matter how crowded the node is. Steal only appears when you and a neighbour want the CPU at the same moment. This is why a quiet VPS can look perfect on Sunday morning and fall apart at Tuesday lunchtime.

    Second, steal accounting depends on the hypervisor exposing it. It is a paravirtual feature, negotiated through a CPUID bit. If the platform underneath you does not expose steal time, the guest will report a flat zero forever even under heavy contention, and you would have to look at hypervisor-side metrics instead. Run systemd-detect-virt to see what you are actually on. If it prints kvm or xen, steal time is meaningful. If it prints lxc or another container type, you are not on a hypervisor at all and steal time is the wrong tool entirely.

    What "shared vCPU" means in practice

    A physical node might carry, for example, two Intel Xeon Gold 6138 processors. Intel's published specification for that part is 20 cores and 40 threads at a 2.00 GHz base and 3.70 GHz maximum turbo. A dual-socket box therefore presents 80 hardware threads. If every vCPU sold on that node were pinned one-to-one to a hardware thread, the machine could host exactly 80 single-core VPS instances and the price would look nothing like what you paid.

    Instead, providers sell more vCPUs than there are threads, on the reasonable assumption that most VPS instances are idle most of the time. The average small WordPress site uses its CPU in short bursts measured in milliseconds and sleeps the rest of the second. Overselling that idle capacity is not fraud. It is the entire business model of affordable virtualisation, and it is why a 1 vCPU instance costs a few dollars instead of a few hundred.

    It becomes a problem in exactly one situation: when enough tenants want the CPU simultaneously that the queue gets long. Then steal time appears in your guest, and your page render that normally takes 180 milliseconds takes 400.

    How to Check Steal Time on a VPS

    Four tools, in increasing order of usefulness. All of them read the same underlying counters, so they will agree with each other.

    1. top, for a five-second look

    Run top and read the line beginning %Cpu(s):. The fields appear in a fixed order: us (user), sy (system), ni (nice), id (idle), wa (I/O wait), hi (hardware interrupts), si (software interrupts) and finally st, which the man page defines as "time stolen from this vm by the hypervisor".

    Press 1 while top is running to break the summary out into one line per vCPU. This matters more than it sounds. Aggregate steal is averaged across all your cores, so on a 4 vCPU plan a single vCPU that is being completely starved shows up as roughly 25 percent aggregate steal, and one that is half starved shows as 12 percent. The per-core view tells you whether the pain is spread evenly or concentrated.

    Press q to exit. For a single snapshot you can pipe to a log with top -bn1.

    2. vmstat, for a short time series

    Run vmstat 1 30. That gives you thirty samples one second apart. The CPU group at the right of the output is us sy id wa st, and newer builds of procps-ng append a gu column for KVM guest code. The man page describes st as "time stolen from a virtual machine".

    Ignore the first row. This trips up almost everyone. The vmstat man page is explicit: "The first report produced gives averages since the last reboot." On a server that has been up for 60 days, that first line is a 60-day average and it will make a badly oversold node look fine. Read from the second row down.

    3. mpstat, for per-core detail

    mpstat comes from the sysstat package, so install it first with apt install sysstat or dnf install sysstat. Then run mpstat -P ALL 1 5 for five one-second samples across every online CPU.

    The sysstat manual defines %steal as the "percentage of time spent in involuntary wait by the virtual CPU or CPUs while the hypervisor was servicing another virtual processor". Columns come out in a stable order: CPU, %usr, %nice, %sys, %iowait, %irq, %soft, %steal, %guest, %gnice, %idle. The first row of each block is the average across all processors, followed by one row per core.

    This is the tool to use when top has told you something is wrong and you want to know which vCPU is suffering.

    4. /proc/stat, for scripting

    Everything above reads /proc/stat. If you are building your own monitoring, read it directly, because the field order is defined by the kernel and does not shift between tool versions the way column positions in vmstat output can.

    The kernel documentation for the proc filesystem gives the order of the counters on the cpu line as: user, nice, system, idle, iowait, irq, softirq, steal, guest, guest_nice. Steal is therefore the eighth number, which is field 9 once you count the cpu label itself. Values are cumulative since boot and measured in USER_HZ, normally hundredths of a second.

    Because they are cumulative counters, a single read is useless. You need two reads and a subtraction:

    S1=$(awk '/^cpu / { print $9 }' /proc/stat); T1=$(awk '/^cpu / { t=0; for (i=2; i<=9; i++) t+=$i; print t }' /proc/stat); sleep 10; S2=$(awk '/^cpu / { print $9 }' /proc/stat); T2=$(awk '/^cpu / { t=0; for (i=2; i<=9; i++) t+=$i; print t }' /proc/stat); echo "scale=3; 100*($S2-$S1)/($T2-$T1)" | bc

    Note the loop stops at field 9 rather than summing the whole line. The guest and guest_nice counters are also accumulated inside user and nice, so summing every field double-counts them. On an ordinary VPS both read zero and it makes no difference, but if you run nested virtualisation it will skew your percentage.

    Need more power? Move up to a VPS

    Dedicated resources, full root access, and NVMe storage. From $3.49/mo, ready in minutes.

    Compare VPS plans

    Reading the Number: A Steal Time Interpretation Table

    Before the table, the honest disclaimer that other pages leave out: these bands are rules of thumb, not standards. There is no kernel document, no RFC and no provider SLA that defines an acceptable steal percentage. They are the thresholds experienced operators use because they correlate well with user-visible latency, and you should treat them as a starting point for investigation rather than a verdict.

    Sustained steal What is happening What to do
    Under 1% Effectively no contention. Your vCPU gets a core essentially whenever it asks. Nothing. If the server feels slow, the cause is somewhere else. Skip to the next section.
    1-5% Normal life on a shared vCPU node. Brief queueing at busy moments, invisible to most workloads. Log it so you have a baseline, then ignore it. This is what you bought.
    5-10% The node is busy enough to cost you measurable latency. Roughly one CPU request in fifteen is waiting. Start a 24-hour log. Correlate the peaks with your own traffic. If the steal peaks are not aligned with your traffic peaks, it is a neighbour, not you.
    10-25% You are meaningfully not getting the CPU you pay for. Response times will be visibly inconsistent. Open a ticket with your logs attached and ask to be moved to a less loaded node.
    Above 25% The node is in trouble. A quarter or more of your CPU requests are queued behind other tenants. Escalate immediately. If the provider cannot migrate you within a day or two, start planning your exit.

    One qualifier on all of the above: spikes are not the same as sustained load. A 40 percent steal reading for three seconds during a neighbour's backup window is noise. Five percent every weekday between 14:00 and 18:00 is a pattern, and the pattern is what matters.

    Measure Over 24 Hours, Not 30 Seconds

    Oversubscription is a peak-hour phenomenon. If you SSH in at 03:00, run vmstat 1 10, see 0.2 percent steal and conclude the node is healthy, you have measured the one time of day when nobody is using it. This is the single biggest mistake people make when evaluating a host.

    Here is a protocol that takes five minutes to set up and produces something you can actually attach to a support ticket.

    Option A: cron plus vmstat

    Add a root cron entry that samples once a minute and appends to a log:

    * * * * * /usr/bin/vmstat 1 2 | /usr/bin/tail -1 | /usr/bin/awk -v ts="$(/bin/date -Is)" '{ print ts, $13, $14, $15, $16, $17 }' >> /var/log/steal.log

    Two things to know about that line. Using vmstat 1 2 and taking the last row skips the since-boot average, so you get a real one-second sample. And field 17 is st in the classic procps-ng column layout, with 13 through 16 being us sy id wa. Column positions are exactly the sort of thing that changes between distribution versions, so run vmstat 1 2 once by hand and count before you trust the numbers.

    Also worth knowing, because it costs people an afternoon: a percent sign in a crontab command is special. The crontab man page states that a percent character "unless escaped with a backslash, will be changed into newline characters, and all data after the first % will be sent to the command as standard input". If you use a date format string containing percent signs, escape every one of them.

    Option B: sar, the tool built for this

    The sysstat package includes a background collector that has been recording CPU statistics on a schedule since long before anyone called it observability. Once it is enabled, sar -u replays the day's CPU history including %steal, with no scripting on your part.

    • Install sysstat, then make sure the collector is running. Some distributions ship it disabled by default, so check /etc/default/sysstat or enable the sysstat service through systemd.
    • Wait a day. Collection typically runs every ten minutes, configured in the package's cron job or timer.
    • Run sar -u for today, or sar -u -f /var/log/sa/sa15 to read a specific day's file. The default data directory is /var/log/sa, though some distributions relocate it.
    • Narrow to a window with -s and -e, for example sar -u -s 14:00:00 -e 18:00:00. Note that sar defaults to 08:00 through 18:00, so if your problem is at 22:00 you must ask for it explicitly.

    What to look at once you have the data

    • The distribution, not the mean. An average of 3 percent that consists of 22 hours at 0.1 percent and two hours at 30 percent is a broken node, and the mean hides it completely.
    • Time of day. Plot or sort by hour. Neighbour-driven steal clusters around business hours in the node's region and around common backup windows.
    • Correlation with your own load. Compare the steal log against your web server access log. If your traffic is flat while steal climbs, the contention is not yours.
    • Day of week. Some nodes are fine Monday to Friday and terrible on the weekend, or the reverse, depending on what the other tenants run.

    Why Your VPS Is Slow When Steal Time Is Near Zero

    This section exists because steal time has become the fashionable thing to blame, and a lot of people now diagnose an oversold node when they actually have a slow query. If your steal reading is under one percent, the CPU is not your problem. Look here instead.

    Disk latency

    Run iostat -x 1 and read await, which the sysstat manual defines as the average time in milliseconds for I/O requests to be served, including queue time. Also read r_await and w_await separately, since read and write behaviour often diverge sharply. On NVMe you want single-digit milliseconds; tens of milliseconds under normal load means the storage is contended or the workload is doing far more I/O than you think. Ignore %util on modern SSDs. The man page itself warns that for devices serving requests in parallel, "this number does not reflect their performance limits".

    Single-thread CPU speed

    Most web requests are single-threaded. A PHP page render, a Python view, a Node handler: one core, start to finish. Eight slow vCPUs will lose to two fast ones for that workload every single time. Steal time tells you whether you got a core. It says nothing about how fast that core was. Check lscpu for the model and compare its published base and turbo clocks, and remember that all-core sustained load pushes any server CPU toward its base frequency, not its turbo.

    The noisy neighbour that does not show as steal

    Two real cases where you lose performance with a steal reading of zero. If your vCPU is scheduled on a hardware thread whose SMT sibling is running a busy neighbour, your vCPU is running, so nothing is counted as stolen, but each cycle does less work because the two threads share execution resources. Similarly, a neighbour saturating memory bandwidth or thrashing the shared L3 cache slows your instructions down without ever making them wait for a core. Steal time cannot see either. Sustained throughput benchmarking can.

    PHP-FPM worker exhaustion

    Classic symptom: the site is either instant or takes eight seconds, with nothing in between, and CPU sits at 20 percent. That is a queue, not a shortage of compute. Every FPM worker is busy waiting on something slow, usually the database or an outbound HTTP call, and new requests sit in the listen backlog. Check the FPM status page or the slow log, and read our guide on how many websites you can host on a VPS for how to size pm.max_children against real memory.

    Memory pressure and swap

    Run free -m and vmstat 1 10, and watch the si and so columns. Any sustained non-zero swap-in and swap-out on a web server means you are paging to disk, and disk is several orders of magnitude slower than RAM. This looks like a CPU problem in every dashboard and is not one.

    Container CPU throttling

    If systemd-detect-virt told you that you are in a container rather than a full VM, hard CPU caps are enforced by cgroups and do not appear as steal. Read /sys/fs/cgroup/cpu.stat and look at nr_throttled and throttled_usec. A rising nr_throttled means you are hitting a quota ceiling defined in cpu.max, which is a completely different conversation with your provider than an oversold node.

    The quick decision tree

    • If steal is above 5 percent at your traffic peak, then it is the node. Go to the next section.
    • If steal is low and await is high, then it is storage. Reduce I/O, add caching, or move to faster disk.
    • If steal and await are both low but CPU sits pegged at 100 percent on one core, then it is your application. Profile it.
    • If everything is low and requests still queue, then it is worker or connection limits. Check PHP-FPM, database max_connections, and the listen backlog.
    • If the server is fast at the shell but slow in a browser, then it is network or TLS, not the VPS. Test with curl -w and mtr from the user's region.

    VPS Oversubscription, Described by a Host That Does It

    No hosting provider wants to write this paragraph, which is roughly why it is worth writing. Shared vCPU means your cores are scheduled against other tenants' cores. Every provider in the budget and mid-market tier does this. The differences between providers are not whether they oversubscribe, but the ratio they run, how carefully they watch it, and what they do when a node goes bad.

    A well-run node oversubscribes conservatively and monitors host-side CPU ready and run-queue metrics so that problems are found before customers file tickets. A badly run node is packed until complaints arrive, and the complaints are answered with "we see no issue on our end". You can usually tell which kind you are on within one week of logging.

    There is a second failure mode that is nobody's fault: correlated peaks. A node whose tenants are all European e-commerce sites will be fine at 04:00 and strained at 20:00, even at a sane ratio, simply because everybody wants the CPU at once. This is why a node can be genuinely well provisioned on paper and still deliver bad steal time for two hours a day, and why the fix is often a migration to a differently balanced node rather than a bigger plan.

    The honest position for a provider is not "we never oversubscribe". It is: here is how to measure it, on our machines as much as anyone's, and if you measure something bad on ours, open a ticket and we will look at the node. A provider who tells you steal time does not apply to them is either running dedicated cores and charging accordingly, or is not being straight with you.

    What to Actually Do About High Steal Time

    You cannot fix steal time from inside the guest. No kernel parameter, nice value or CPU governor setting will get you a core the host has given to somebody else. Everything you can do is either a conversation with your provider or a change of provider.

    1. Open a ticket, with evidence

    The difference between a ticket that gets a node migration and a ticket that gets a copy-paste reply is the attachment. Send:

    • Your 24-hour log or sar -u output, in plain text, with timestamps.
    • The specific hours where steal exceeded your threshold, called out explicitly rather than left for support to find.
    • Evidence that the steal is not correlated with your own load, for example a request count per hour from your access log alongside the steal figures.
    • Output of mpstat -P ALL 1 5 taken during a bad window, showing whether the problem is one vCPU or all of them.
    • A clear ask. "Please investigate node load and migrate this VPS to a less contended node" is a request an engineer can act on. "My server is slow" is not.

    2. Ask specifically for a node migration

    This is the single most effective request and most customers never make it. Providers usually run many nodes in a location, and their load profiles differ. Moving a VM between nodes is routine work, typically a short reboot, and a reasonable host will do it when you present data. If a provider refuses to discuss node placement at all, that is your answer about how they operate.

    3. Consider whether a different plan actually helps

    Upgrading from 2 vCPU to 4 vCPU on the same oversold node gives you more virtual cores contending in the same queue. It sometimes helps, because more vCPUs mean more chances to be scheduled, but it is not a fix and you should not let anyone sell it to you as one. Buying a dedicated-vCPU product, from your current provider or another, is a fix. So is moving to a different location.

    4. Leave, and do it methodically

    If two tickets over two weeks produce no measurable change, the relationship is not going to improve. Migrate deliberately: bring up the new server, reduce your DNS TTL a day in advance, sync data, test on the new IP with a hosts-file override, then switch. Take the opportunity to redo the basics properly on the new box with our VPS security hardening checklist. And before you commit to the next provider, run the same 24-hour steal measurement on their trial instance. Measuring before you migrate is much cheaper than measuring afterwards.

    Dedicated vCPU vs Shared vCPU

    A dedicated vCPU product reserves physical CPU capacity for your instance rather than scheduling it against neighbours. Providers implement this differently, and the terminology is not standardised, so read the product page carefully: "dedicated CPU", "CPU-optimised" and "guaranteed vCPU" can all mean subtly different guarantees.

    What you are buying is predictability, not raw speed. A dedicated vCPU is usually the same silicon as a shared one. What changes is the variance: your p50 response time may barely move, while your p99 improves dramatically. For anything where consistency is the product, that is worth real money. For a brochure site, it is worth nothing.

    Workload Shared vCPU Why
    Brochure site, blog, small WordPress Fine Bursty, milliseconds of CPU per request, mostly served from cache. Dedicated cores are wasted money here.
    WooCommerce or another dynamic store Usually fine Cart and checkout pages bypass caching, so watch steal during promotions. Upgrade if p99 checkout latency suffers.
    API backend with a latency SLA Depends on the SLA If you promise a p99 to somebody else, you cannot afford variance you do not control.
    CI/CD runner, build server Poor fit Sustained all-core load for minutes at a time. This is precisely the workload oversubscription punishes, and slow builds cost developer hours.
    Video encoding, ffmpeg batch jobs Poor fit Hours of 100 percent CPU. You will feel every percent of steal, and you may also annoy your neighbours.
    Game server (Minecraft, source engine, similar) Poor fit Tick-rate workloads need a core available every tick. Steal shows up directly as lag spikes that players notice.
    Busy database primary Depends on scale Small databases are fine. Once query concurrency is high, scheduling delay compounds through connection pools.
    Dev box, staging, bots, cron jobs Ideal Idle almost all the time. Paying for reserved cores here is paying for nothing.

    The rule underneath the table: if your CPU usage graph looks like occasional spikes, shared vCPU is correct and cheaper. If it looks like a plateau, buy dedicated. If you do not know which it looks like, that is what the 24-hour log is for.

    Where Devoster Fits, Honestly

    Our VPS plans are KVM with full root access, running on Intel Xeon Gold 6138 hardware with NVMe storage, a 1 Gbps link and unlimited bandwidth, from Nano at 3.49 dollars a month up to Mega at 34.99. Every plan includes an IPv4 address, and migration help is free.

    What we are not going to tell you is that we have somehow repealed the economics of shared virtualisation. Our plans are shared vCPU, priced like shared vCPU. What we will tell you is what to do about it: measure. Run the 24-hour protocol above on your Devoster VPS the same way you would on anyone else's. If you find sustained steal that does not correlate with your own traffic, open a ticket with the log attached and ask us to look at the node. That is a request we can act on, and we would rather act on it than read about it in a review.

    Devoster is the wrong choice if you need contractually guaranteed dedicated cores, if you need a location far from US-South, or if you do not want to administer a Linux server at all. In that last case, shared or managed WordPress hosting is a better fit, and if you are weighing the general trade-offs, our breakdown of what cheap VPS hosting actually buys you covers the rest of the corners that budget providers cut.

    Have questions? Get in touch

    Not sure which plan fits or how crypto billing works for you? We're here to help.

    Contact us

    Frequently Asked Questions

    What is a normal CPU steal time on a VPS?

    Sustained readings under 1 percent are excellent, and 1 to 5 percent is ordinary on any shared vCPU plan. Between 5 and 10 percent you are losing measurable latency and should collect data. Above 10 percent for hours at a time, the node is oversold. These are practitioner rules of thumb rather than a published standard, so treat them as thresholds for investigation.

    How do I check steal time on a VPS?

    Run top and read the st value on the CPU summary line, pressing 1 to see each vCPU separately. For a short time series use vmstat 1 30 and read the st column, ignoring the first row because it is a since-boot average. For per-core detail install sysstat and run mpstat -P ALL 1 5.

    What is steal time in Linux, exactly?

    It is the time your virtual CPU was ready to run but the hypervisor did not give it a physical core. The kernel's /proc/stat documentation calls it involuntary wait. On KVM the host reports it to the guest through a paravirtual MSR in nanoseconds, and time the vCPU spends idle is explicitly excluded, so any non-zero value means real queued work was delayed.

    Can I reduce steal time from inside my VPS?

    No. Steal time is decided by the host scheduler, which your guest kernel has no influence over. You can reduce how much you suffer from it by caching more aggressively, moving batch jobs off peak hours, or reducing your CPU footprint, but the number itself only changes when your provider moves you to a less loaded node or you move to a dedicated-vCPU product.

    Why is my VPS slow if steal time is zero?

    Because steal time only measures one failure mode. Check disk latency with iostat -x 1 and read await, check for swapping in the si and so columns of vmstat, check whether PHP-FPM or your database is out of workers or connections, and check single-thread CPU speed. Memory bandwidth contention from a neighbour also slows you down without registering any steal at all.

    Is dedicated vCPU worth the extra cost?

    It is worth it when your CPU graph is a plateau rather than a series of spikes: CI runners, video encoding, game servers with tick-rate requirements, and busy database primaries. It is wasted money for brochure sites, blogs, small WordPress installs, staging boxes and bots, which are idle most of the time and burst for milliseconds when they are not.

    Ready to Experience Devoster?

    Join thousands of satisfied customers with transparent pricing and lightning-fast hosting.

    We value your privacy

    We use essential cookies to make our site work, and optional analytics cookies to understand how you use Devoster and improve our services. You can accept all cookies, or adjust your preferences.

    Read more in our Cookie Policy and Privacy Policy. You can change your choices at any time.