{"id":1279,"date":"2026-10-02T22:35:16","date_gmt":"2026-10-02T22:35:16","guid":{"rendered":"https:\/\/virtualserversvps.com\/blog\/?p=1279"},"modified":"2026-10-02T22:35:16","modified_gmt":"2026-10-02T22:35:16","slug":"postgresql-autovacuum-tuning-small-vps-bloat-io","status":"publish","type":"post","link":"https:\/\/virtualserversvps.com\/blog\/postgresql-autovacuum-tuning-small-vps-bloat-io\/","title":{"rendered":"PostgreSQL Autovacuum Tuning on a Small VPS: Preventing Bloat Without Starving I\/O"},"content":{"rendered":"<div style=\"padding:20px\">\n\n\n<p class=\"wp-block-paragraph\">Autovacuum is the most misunderstood background process on a small PostgreSQL VPS. Left at defaults sized for large dedicated servers, it either runs too rarely \u2014 letting dead tuples bloat tables and slow every query \u2014 or fires too aggressively, consuming the limited I\/O and CPU a cheap plan can spare. This guide explains what autovacuum actually does under memory pressure, then gives parameter values suited to a 2\u20134 GB instance serving a production web workload.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Storage IOPS is the ceiling that makes autovacuum tuning matter; a plan with slow random writes will always struggle regardless of settings. Verify the storage path behind your plan in <a href=\"https:\/\/virtualserversvps.com\/\">our VPS comparison table<\/a> first.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Why Defaults Fail on Small Instances<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Default autovacuum triggers when a table accumulates roughly 20% dead tuples plus a fixed threshold of 50 rows. On a large table that is fine; on a small, heavily updated table the trigger may never fire until the table is already badly bloated. Meanwhile, the default <code>autovacuum_max_workers<\/code> of 3 and generous <code>cost_limit<\/code> settings let vacuum workers compete with your application for the tiny I\/O budget of a budget VPS. The result is a database that is simultaneously bloated and I\/O-starved.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Measuring Bloat Before You Tune<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Never tune blindly. Two queries tell you whether you have a bloat problem and which tables cause it:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>-- Dead tuples and last vacuum per table\nSELECT relname, n_live_tup, n_dead_tup,\n       last_autovacuum, last_autoanalyze\nFROM pg_stat_user_tables\nORDER BY n_dead_tup DESC LIMIT 10;\n\n-- Estimated bloat via the pgstattuple extension\nCREATE EXTENSION IF NOT EXISTS pgstattuple;\nSELECT * FROM pgstattuple('your_table');<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">If <code>n_dead_tup<\/code> climbs between vacuums and <code>last_autovacuum<\/code> is stale, autovacuum is not keeping up. That is your signal to lower the scale factor and raise worker throughput, not to run a manual <code>VACUUM FULL<\/code> that locks the table and rewrites it.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Per-Table Thresholds Beat Global Settings<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Global changes affect every table, including ones that never churn. On a small server it is more precise to set aggressive thresholds on the specific high-write tables and leave the rest alone:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>ALTER TABLE sessions SET (\n  autovacuum_vacuum_scale_factor = 0.05,\n  autovacuum_vacuum_threshold = 50,\n  autovacuum_analyze_scale_factor = 0.02\n);<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">A 5% scale factor means vacuum runs four times more often than the 20% default on that table, keeping dead tuples low and index scans fast. Reserve this treatment for tables with frequent UPDATE or DELETE activity \u2014 session stores, job queues, and log-style tables are typical candidates.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Cost-Based Throttling for a Small I\/O Budget<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Autovacuum&#8217;s cost parameters decide how hard it is allowed to work. On a VPS with limited IOPS, the goal is steady low-intensity work rather than bursts that starve the application. Lower the cost limit and raise the delay so vacuum spreads its work out:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>ALTER SYSTEM SET autovacuum_vacuum_cost_limit = 200;\nALTER SYSTEM SET autovacuum_vacuum_cost_delay = '10ms';\nALTER SYSTEM SET autovacuum_max_workers = 2;\nSELECT pg_reload_conf();<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Two workers instead of three reduces concurrent I\/O contention; the lower cost limit caps how much each worker can spend per cycle; the delay gives the application breathing room between cycles. On NVMe-backed storage you can raise the cost limit back up, since the device handles concurrency better.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Monitoring Whether It Is Working<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">After a week, re-run the dead-tuple query. You want to see <code>n_dead_tup<\/code> staying low relative to <code>n_live_tup<\/code> and <code>last_autovacuum<\/code> updating regularly. Rising bloat with frequent vacuums means the cost limit is still too high for your storage; bloat with rare vacuums means the thresholds are still too loose. Adjust one variable at a time and observe for several days, because autovacuum operates on a slow timescale and a single snapshot misleads.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Preventing Autovacuum From Being Interrupted<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">A vacuum that starts and never finishes is worse than one that never starts, because it holds resources and defers the next attempt. Long-running read transactions block vacuum from reclaiming tuples, so a single idle-in-transaction connection can stall cleanup indefinitely. Check for the culprits regularly:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>SELECT pid, state, now() - xact_start AS duration, query\nFROM pg_stat_activity\nWHERE state  'idle' OR xact_start IS NOT NULL\nORDER BY duration DESC;<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Any transaction open for hours is actively preventing dead-tuple reclamation. Fix the application bug or set <code>idle_in_transaction_session_timeout<\/code> so abandoned transactions are closed automatically. On a small VPS this single setting often does more for bloat control than any autovacuum parameter.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">The Wraparound Safety Net<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Autovacuum also advances the transaction ID counter that PostgreSQL relies on. If autovacuum cannot keep up for long enough, you approach transaction wraparound and the database forces an emergency vacuum that stops writes entirely. This is rare but catastrophic, and on a small instance with aggressive churn it is a realistic risk. Monitor the age of the oldest unfrozen transaction:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>SELECT datname, age(datfrozenxid) AS xid_age\nFROM pg_database ORDER BY xid_age DESC;<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Keep this number far below the wraparound limit. If it climbs steadily despite frequent autovacuum runs, the vacuum is not completing \u2014 revisit the blocked-transaction check above before touching any other parameter.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">A Practical Starting Configuration<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">For a 2\u20134 GB VPS running a production web workload, a sensible baseline combines per-table thresholds on the churn-heavy tables with conservative global throttling. Set the global cost limit low enough that vacuum never starves the application, then rely on the per-table scale factors to keep dead tuples down where it matters. Write the global changes to <code>postgresql.auto.conf<\/code> via <code>ALTER SYSTEM<\/code>, and keep per-table overrides in a migration or schema-management file so they survive a database rebuild.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">If bloat stays controlled but queries are still slow, the bottleneck has moved to memory or query planning. And if the table genuinely outgrows the plan, that is a purchasing decision, not a configuration one: compare storage and RAM guarantees across providers in <a href=\"https:\/\/virtualserversvps.com\/#providers\">the comparison section of our site<\/a> before you spend another evening tuning a ceiling you cannot raise. Document every value you change and the date \u2014 autovacuum tuning is a slow feedback loop, and without a record you will repeat experiments. Measure, adjust one thing, wait, and measure again.<\/p>\n\n\n<\/div>\n","protected":false},"excerpt":{"rendered":"<p>Autovacuum is the most misunderstood background process on a small PostgreSQL VPS. Left at defaults sized for large dedicated servers, it either runs too rarely \u2014 letting dead tuples bloat&#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":0,"footnotes":""},"categories":[3],"tags":[],"class_list":["post-1279","post","type-post","status-publish","format-standard","hentry","category-performance-optimization"],"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>PostgreSQL Autovacuum Tuning on a Small VPS: Preventing Bloat Without Starving I\/O - Virtual Servers VPS Blog<\/title>\n<meta name=\"description\" content=\"Autovacuum is the most misunderstood background process on a small PostgreSQL VPS. Left at defaults sized for large dedicated servers, it either runs too\" \/>\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\/postgresql-autovacuum-tuning-small-vps-bloat-io\/\" \/>\n<meta property=\"og:locale\" content=\"en_US\" \/>\n<meta property=\"og:type\" content=\"article\" \/>\n<meta property=\"og:title\" content=\"PostgreSQL Autovacuum Tuning on a Small VPS: Preventing Bloat Without Starving I\/O\" \/>\n<meta property=\"og:description\" content=\"PostgreSQL Autovacuum Tuning on a Small VPS: Preventing Bloat Without Starving I\/O\" \/>\n<meta property=\"og:url\" content=\"https:\/\/virtualserversvps.com\/blog\/postgresql-autovacuum-tuning-small-vps-bloat-io\/\" \/>\n<meta property=\"og:site_name\" content=\"Virtual Servers VPS Blog\" \/>\n<meta property=\"article:published_time\" content=\"2026-10-02T22:35:16+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\/postgresql-autovacuum-tuning-small-vps-bloat-io\/\",\"url\":\"https:\/\/virtualserversvps.com\/blog\/postgresql-autovacuum-tuning-small-vps-bloat-io\/\",\"name\":\"PostgreSQL Autovacuum Tuning on a Small VPS: Preventing Bloat Without Starving I\/O - Virtual Servers VPS Blog\",\"isPartOf\":{\"@id\":\"https:\/\/virtualserversvps.com\/blog\/#website\"},\"datePublished\":\"2026-10-02T22:35:16+00:00\",\"author\":{\"@id\":\"https:\/\/virtualserversvps.com\/blog\/#\/schema\/person\/82a299a8284a66ff49f97c74684724a0\"},\"description\":\"Autovacuum is the most misunderstood background process on a small PostgreSQL VPS. Left at defaults sized for large dedicated servers, it either runs too\",\"breadcrumb\":{\"@id\":\"https:\/\/virtualserversvps.com\/blog\/postgresql-autovacuum-tuning-small-vps-bloat-io\/#breadcrumb\"},\"inLanguage\":\"en-US\",\"potentialAction\":[{\"@type\":\"ReadAction\",\"target\":[\"https:\/\/virtualserversvps.com\/blog\/postgresql-autovacuum-tuning-small-vps-bloat-io\/\"]}]},{\"@type\":\"BreadcrumbList\",\"@id\":\"https:\/\/virtualserversvps.com\/blog\/postgresql-autovacuum-tuning-small-vps-bloat-io\/#breadcrumb\",\"itemListElement\":[{\"@type\":\"ListItem\",\"position\":1,\"name\":\"Home\",\"item\":\"https:\/\/virtualserversvps.com\/blog\/\"},{\"@type\":\"ListItem\",\"position\":2,\"name\":\"PostgreSQL Autovacuum Tuning on a Small VPS: Preventing Bloat Without Starving I\/O\"}]},{\"@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":"PostgreSQL Autovacuum Tuning on a Small VPS: Preventing Bloat Without Starving I\/O - Virtual Servers VPS Blog","description":"Autovacuum is the most misunderstood background process on a small PostgreSQL VPS. Left at defaults sized for large dedicated servers, it either runs too","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\/postgresql-autovacuum-tuning-small-vps-bloat-io\/","og_locale":"en_US","og_type":"article","og_title":"PostgreSQL Autovacuum Tuning on a Small VPS: Preventing Bloat Without Starving I\/O","og_description":"PostgreSQL Autovacuum Tuning on a Small VPS: Preventing Bloat Without Starving I\/O","og_url":"https:\/\/virtualserversvps.com\/blog\/postgresql-autovacuum-tuning-small-vps-bloat-io\/","og_site_name":"Virtual Servers VPS Blog","article_published_time":"2026-10-02T22:35:16+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\/postgresql-autovacuum-tuning-small-vps-bloat-io\/","url":"https:\/\/virtualserversvps.com\/blog\/postgresql-autovacuum-tuning-small-vps-bloat-io\/","name":"PostgreSQL Autovacuum Tuning on a Small VPS: Preventing Bloat Without Starving I\/O - Virtual Servers VPS Blog","isPartOf":{"@id":"https:\/\/virtualserversvps.com\/blog\/#website"},"datePublished":"2026-10-02T22:35:16+00:00","author":{"@id":"https:\/\/virtualserversvps.com\/blog\/#\/schema\/person\/82a299a8284a66ff49f97c74684724a0"},"description":"Autovacuum is the most misunderstood background process on a small PostgreSQL VPS. Left at defaults sized for large dedicated servers, it either runs too","breadcrumb":{"@id":"https:\/\/virtualserversvps.com\/blog\/postgresql-autovacuum-tuning-small-vps-bloat-io\/#breadcrumb"},"inLanguage":"en-US","potentialAction":[{"@type":"ReadAction","target":["https:\/\/virtualserversvps.com\/blog\/postgresql-autovacuum-tuning-small-vps-bloat-io\/"]}]},{"@type":"BreadcrumbList","@id":"https:\/\/virtualserversvps.com\/blog\/postgresql-autovacuum-tuning-small-vps-bloat-io\/#breadcrumb","itemListElement":[{"@type":"ListItem","position":1,"name":"Home","item":"https:\/\/virtualserversvps.com\/blog\/"},{"@type":"ListItem","position":2,"name":"PostgreSQL Autovacuum Tuning on a Small VPS: Preventing Bloat Without Starving I\/O"}]},{"@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\/1279","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=1279"}],"version-history":[{"count":1,"href":"https:\/\/virtualserversvps.com\/blog\/wp-json\/wp\/v2\/posts\/1279\/revisions"}],"predecessor-version":[{"id":1281,"href":"https:\/\/virtualserversvps.com\/blog\/wp-json\/wp\/v2\/posts\/1279\/revisions\/1281"}],"wp:attachment":[{"href":"https:\/\/virtualserversvps.com\/blog\/wp-json\/wp\/v2\/media?parent=1279"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/virtualserversvps.com\/blog\/wp-json\/wp\/v2\/categories?post=1279"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/virtualserversvps.com\/blog\/wp-json\/wp\/v2\/tags?post=1279"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}