{"id":1387,"date":"2026-10-13T23:07:12","date_gmt":"2026-10-13T23:07:12","guid":{"rendered":"https:\/\/virtualserversvps.com\/blog\/?p=1387"},"modified":"2026-10-13T23:07:12","modified_gmt":"2026-10-13T23:07:12","slug":"vps-cgroups-v2-resource-limits-systemd","status":"publish","type":"post","link":"https:\/\/virtualserversvps.com\/blog\/vps-cgroups-v2-resource-limits-systemd\/","title":{"rendered":"cgroups v2 on a VPS: Capping CPU, Memory and I\/O per Service with systemd"},"content":{"rendered":"<p class=\"wp-block-paragraph\">A single runaway service \u2014 a rebuild, a backup, a memory leak \u2014 can starve everything else on a small VPS. cgroups v2 lets you bound each service&#8217;s CPU, memory and I\/O so one process cannot take the whole box down. This is a practical setup using systemd units and slices, with verification at every step.<\/p><h2 class=\"wp-block-heading\">Confirm you are on cgroup v2<\/h2><pre class=\"wp-block-code\"><code>stat -fc %T \/sys\/fs\/cgroup\n# cgroup2fs  =&gt; v2 (unified)\n# tmpfs      =&gt; v1 (legacy, hybrid)\nsystemctl --version\ncat \/sys\/fs\/cgroup\/cgroup.controllers<\/code><\/pre><p class=\"wp-block-paragraph\">If <code>cgroup.controllers<\/code> lists <code>cpu memory io pids<\/code>, the controllers are available. On v1, the syntax below does not apply and you would use the legacy <code>CPUQuota<\/code>\/<code>MemoryMax<\/code> mapping which is less complete.<\/p><h2 class=\"wp-block-heading\">The three knobs you actually need<\/h2><figure class=\"wp-block-table\"><table><thead><tr><th>Resource<\/th><th>systemd directive<\/th><th>cgroup v2 file<\/th><\/tr><\/thead><tbody><tr><td>CPU bandwidth<\/td><td>CPUQuota=50%<\/td><td>cpu.max<\/td><\/tr><tr><td>Memory ceiling<\/td><td>MemoryMax=512M<\/td><td>memory.max<\/td><\/tr><tr><td>Memory throttle (soft)<\/td><td>MemoryHigh=384M<\/td><td>memory.high<\/td><\/tr><tr><td>I\/O bandwidth<\/td><td>IOReadBandwidthMax=<\/td><td>io.max<\/td><\/tr><tr><td>Process count<\/td><td>TasksMax=256<\/td><td>pids.max<\/td><\/tr><\/tbody><\/table><\/figure><p class=\"wp-block-paragraph\"><code>MemoryHigh<\/code> is the underused one: it throttles and reclaims before <code>MemoryMax<\/code> triggers the OOM killer. Setting only <code>MemoryMax<\/code> lets a service balloon right up to the hard wall and then get killed. Set <code>MemoryHigh<\/code> at ~75% of <code>MemoryMax<\/code> for graceful behaviour.<\/p><h2 class=\"wp-block-heading\">Cap a service with a drop-in override<\/h2><p class=\"wp-block-paragraph\">Never edit unit files from the package manager \u2014 use a drop-in so upgrades do not clobber your changes:<\/p><pre class=\"wp-block-code\"><code>systemctl edit myservice.service\n\n# --- contents ---\n[Service]\nCPUQuota=75%\nMemoryHigh=384M\nMemoryMax=512M\nIOWeight=50\nTasksMax=512\n<\/code><\/pre><pre class=\"wp-block-code\"><code>systemctl daemon-reload\nsystemctl restart myservice\nsystemctl show myservice -p CPUQuota -p MemoryMax -p MemoryHigh<\/code><\/pre><h2 class=\"wp-block-heading\">Group related services into a slice<\/h2><p class=\"wp-block-paragraph\">Apply one limit to a whole tier \u2014 say all web-adjacent services \u2014 by placing them in a custom slice:<\/p><pre class=\"wp-block-code\"><code># \/etc\/systemd\/system\/webtier.slice\n[Slice]\nCPUQuota=150%\nMemoryMax=1G\n\n# in each unit:\n[Service]\nSlice=webtier.slice<\/code><\/pre><p class=\"wp-block-paragraph\">Slices nest, so a parent slice&#8217;s limit applies collectively to its children. This is the clean way to give a workload group a budget without hunting down every individual daemon.<\/p><h2 class=\"wp-block-heading\">Verify the limits are real<\/h2><pre class=\"wp-block-code\"><code># inspect live cgroup values for a running unit\nsystemctl status myservice\ncat \/sys\/fs\/cgroup\/system.slice\/myservice.service\/memory.max\ncat \/sys\/fs\/cgroup\/system.slice\/myservice.service\/cpu.max\n\n# watch memory pressure inside the cgroup\ncat \/sys\/fs\/cgroup\/system.slice\/myservice.service\/memory.events\n#  low \/ high \/ max \/ oom_kill counters tell you which wall you hit<\/code><\/pre><p class=\"wp-block-paragraph\">The <code>memory.events<\/code> counters are the diagnostic gold: <code>high<\/code> incremented means the soft limit throttled it; <code>max<\/code> means it hit the hard ceiling; <code>oom_kill<\/code> means it died. That single file tells you whether to raise the limit or fix the leak.<\/p><ul class=\"wp-block-list\"><li>Start with <code>MemoryHigh<\/code> slightly below <code>MemoryMax<\/code> so services throttle before they die.<\/li><li>Set <code>CPUQuota<\/code> as a percentage of one core: 200% = two cores.<\/li><li>Use <code>TasksMax<\/code> to stop fork bombs from a compromised or buggy service.<\/li><li>Re-verify after every restart; a failed drop-in is silent.<\/li><\/ul><h2 class=\"wp-block-heading\">Testing that the cap actually bites<\/h2><p class=\"wp-block-paragraph\">A limit you have not stress-tested is a limit you do not have. Reproduce load inside the cgroup and watch the accounting counters move:<\/p><pre class=\"wp-block-code\"><code># in one terminal, spin CPU inside the unit's cgroup\nsystemd-run --slice=webtier.slice -p CPUQuota=50% \\\n  stress-ng --cpu 4 --timeout 30s\n\n# in another, watch the throttle happen\nsystemctl status &lt;unit&gt;\ncat \/sys\/fs\/cgroup\/webtier.slice\/cpu.stat\n#  nr_throttled and throttled_usec rising =&gt; the quota is enforced<\/code><\/pre><p class=\"wp-block-paragraph\">For memory, allocate past the limit and confirm the throttle-then-kill ladder behaves as configured. If you only see <code>oom_kill<\/code> and never <code>high<\/code> events, your <code>MemoryHigh<\/code> is too close to <code>MemoryMax<\/code> or unset.<\/p><h2 class=\"wp-block-heading\">Failure modes and how to spot them<\/h2><figure class=\"wp-block-table\"><table><thead><tr><th>Symptom<\/th><th>Cause<\/th><th>Fix<\/th><\/tr><\/thead><tbody><tr><td>Service silently unthrottled<\/td><td>Drop-in not loaded (typo\/daemon-reload missing)<\/td><td>systemctl show &#8211;property to confirm<\/td><\/tr><tr><td>OOM kills despite a MemoryMax<\/td><td>cgroup accounting vs host overcommit<\/td><td>check memory.events + dmesg<\/td><\/tr><tr><td>I\/O limits ignored<\/td><td>io controller not delegated or device mismatch<\/td><td>verify with io.stat and block device<\/td><\/tr><tr><td>CPUQuota set but no effect<\/td><td>service forked outside the unit<\/td><td>use Type=notify \/ KillMode=control-group<\/td><\/tr><\/tbody><\/table><\/figure><p class=\"wp-block-paragraph\">The second row is the one people hit on small VPS instances: a cgroup memory limit is not a reservation, and the host&#8217;s own overcommit policy still governs total memory. If the box is oversubscribed at the host level, your container or service can still be selected by the host kernel even while respecting its own limit. cgroup limits contain blast radius; they do not conjure memory that the host does not have.<\/p><p class=\"wp-block-paragraph\">Applied consistently, per-service caps convert a class of &#8216;the whole VPS went down&#8217; incidents into &#8216;this one service was throttled and logged&#8217;. Start with the noisiest, most variable service \u2014 backups, builds, and anything that caches \u2014 and expand from there.<\/p><p class=\"wp-block-paragraph\">Two operational habits make the whole scheme durable. First, keep the limits in version control: a drop-in committed alongside the service definition is reviewable and reproducible, while one typed directly onto a host disappears at rebuild. Second, emit the cgroup counters \u2014 <code>memory.events<\/code>, <code>cpu.stat<\/code>, <code>io.stat<\/code> \u2014 into your monitoring so a throttle is visible as a trend rather than discovered during an incident. A cap that silently fires without a signal just moves the surprise later.<\/p><p class=\"wp-block-paragraph\">Layered with the host-level memory policy on a small instance, this gives you a predictable failure mode: the constrained service degrades, everything else keeps serving, and you get a log line telling you exactly which wall was hit. That is a far better day than the alternative.<\/p><p class=\"wp-block-paragraph\">Resource caps are the difference between one slow service and a full outage. Combined with sensible swap and OOM policy on a small instance, they turn unpredictable host failures into contained, logged events. If you would rather not hand-tune this on every host, a <a href=\"https:\/\/virtualserversvps.com\/cloud-vps-benefits\">cloud VPS tuned for consistent resource isolation<\/a> is the better starting point.<\/p>","protected":false},"excerpt":{"rendered":"<p>A single runaway service \u2014 a rebuild, a backup, a memory leak \u2014 can starve everything else on a small VPS. cgroups v2 lets you bound each service&#8217;s CPU, memory&#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-1387","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>cgroups v2 on a VPS: Capping CPU, Memory and I\/O per Service with systemd - Virtual Servers VPS Blog<\/title>\n<meta name=\"description\" content=\"A single runaway service \u2014 a rebuild, a backup, a memory leak \u2014 can starve everything else on a small VPS. cgroups v2 lets you bound each service&#039;s CPU,\" \/>\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\/vps-cgroups-v2-resource-limits-systemd\/\" \/>\n<meta property=\"og:locale\" content=\"en_US\" \/>\n<meta property=\"og:type\" content=\"article\" \/>\n<meta property=\"og:title\" content=\"cgroups v2 on a VPS: Capping CPU, Memory and I\/O per Service with systemd\" \/>\n<meta property=\"og:description\" content=\"cgroups v2 on a VPS: Capping CPU, Memory and I\/O per Service with systemd\" \/>\n<meta property=\"og:url\" content=\"https:\/\/virtualserversvps.com\/blog\/vps-cgroups-v2-resource-limits-systemd\/\" \/>\n<meta property=\"og:site_name\" content=\"Virtual Servers VPS Blog\" \/>\n<meta property=\"article:published_time\" content=\"2026-10-13T23:07:12+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\/vps-cgroups-v2-resource-limits-systemd\/\",\"url\":\"https:\/\/virtualserversvps.com\/blog\/vps-cgroups-v2-resource-limits-systemd\/\",\"name\":\"cgroups v2 on a VPS: Capping CPU, Memory and I\/O per Service with systemd - Virtual Servers VPS Blog\",\"isPartOf\":{\"@id\":\"https:\/\/virtualserversvps.com\/blog\/#website\"},\"datePublished\":\"2026-10-13T23:07:12+00:00\",\"author\":{\"@id\":\"https:\/\/virtualserversvps.com\/blog\/#\/schema\/person\/82a299a8284a66ff49f97c74684724a0\"},\"description\":\"A single runaway service \u2014 a rebuild, a backup, a memory leak \u2014 can starve everything else on a small VPS. cgroups v2 lets you bound each service's CPU,\",\"breadcrumb\":{\"@id\":\"https:\/\/virtualserversvps.com\/blog\/vps-cgroups-v2-resource-limits-systemd\/#breadcrumb\"},\"inLanguage\":\"en-US\",\"potentialAction\":[{\"@type\":\"ReadAction\",\"target\":[\"https:\/\/virtualserversvps.com\/blog\/vps-cgroups-v2-resource-limits-systemd\/\"]}]},{\"@type\":\"BreadcrumbList\",\"@id\":\"https:\/\/virtualserversvps.com\/blog\/vps-cgroups-v2-resource-limits-systemd\/#breadcrumb\",\"itemListElement\":[{\"@type\":\"ListItem\",\"position\":1,\"name\":\"Home\",\"item\":\"https:\/\/virtualserversvps.com\/blog\/\"},{\"@type\":\"ListItem\",\"position\":2,\"name\":\"cgroups v2 on a VPS: Capping CPU, Memory and I\/O per Service with systemd\"}]},{\"@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":"cgroups v2 on a VPS: Capping CPU, Memory and I\/O per Service with systemd - Virtual Servers VPS Blog","description":"A single runaway service \u2014 a rebuild, a backup, a memory leak \u2014 can starve everything else on a small VPS. cgroups v2 lets you bound each service's CPU,","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\/vps-cgroups-v2-resource-limits-systemd\/","og_locale":"en_US","og_type":"article","og_title":"cgroups v2 on a VPS: Capping CPU, Memory and I\/O per Service with systemd","og_description":"cgroups v2 on a VPS: Capping CPU, Memory and I\/O per Service with systemd","og_url":"https:\/\/virtualserversvps.com\/blog\/vps-cgroups-v2-resource-limits-systemd\/","og_site_name":"Virtual Servers VPS Blog","article_published_time":"2026-10-13T23:07:12+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\/vps-cgroups-v2-resource-limits-systemd\/","url":"https:\/\/virtualserversvps.com\/blog\/vps-cgroups-v2-resource-limits-systemd\/","name":"cgroups v2 on a VPS: Capping CPU, Memory and I\/O per Service with systemd - Virtual Servers VPS Blog","isPartOf":{"@id":"https:\/\/virtualserversvps.com\/blog\/#website"},"datePublished":"2026-10-13T23:07:12+00:00","author":{"@id":"https:\/\/virtualserversvps.com\/blog\/#\/schema\/person\/82a299a8284a66ff49f97c74684724a0"},"description":"A single runaway service \u2014 a rebuild, a backup, a memory leak \u2014 can starve everything else on a small VPS. cgroups v2 lets you bound each service's CPU,","breadcrumb":{"@id":"https:\/\/virtualserversvps.com\/blog\/vps-cgroups-v2-resource-limits-systemd\/#breadcrumb"},"inLanguage":"en-US","potentialAction":[{"@type":"ReadAction","target":["https:\/\/virtualserversvps.com\/blog\/vps-cgroups-v2-resource-limits-systemd\/"]}]},{"@type":"BreadcrumbList","@id":"https:\/\/virtualserversvps.com\/blog\/vps-cgroups-v2-resource-limits-systemd\/#breadcrumb","itemListElement":[{"@type":"ListItem","position":1,"name":"Home","item":"https:\/\/virtualserversvps.com\/blog\/"},{"@type":"ListItem","position":2,"name":"cgroups v2 on a VPS: Capping CPU, Memory and I\/O per Service with systemd"}]},{"@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\/1387","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=1387"}],"version-history":[{"count":3,"href":"https:\/\/virtualserversvps.com\/blog\/wp-json\/wp\/v2\/posts\/1387\/revisions"}],"predecessor-version":[{"id":1394,"href":"https:\/\/virtualserversvps.com\/blog\/wp-json\/wp\/v2\/posts\/1387\/revisions\/1394"}],"wp:attachment":[{"href":"https:\/\/virtualserversvps.com\/blog\/wp-json\/wp\/v2\/media?parent=1387"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/virtualserversvps.com\/blog\/wp-json\/wp\/v2\/categories?post=1387"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/virtualserversvps.com\/blog\/wp-json\/wp\/v2\/tags?post=1387"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}