<!-- mobian-agent-page publisher="dailydev" canonical="https://daily.dev/posts/postgres-autovacuum-fell-behind-for-11-days-then-one-update-locked-checkout-e5buzagbr" -->

---
title: Postgres Autovacuum Fell Behind for 11 Days. Then One...
description: A production incident postmortem where PostgreSQL autovacuum fell 11 days behind on an `order_items` table, accumulating 847 million dead tuples and 186 GB of...
canonical: https://daily.dev/posts/postgres-autovacuum-fell-behind-for-11-days-then-one-update-locked-checkout-e5buzagbr
twitter:card: summary_large_image
twitter:site: @dailydotdev
og:type: website
og:site_name: daily.dev
og:title: Postgres Autovacuum Fell Behind for 11 Days. Then One UPDATE Locked Checkout | daily.dev
og:description: A production incident postmortem where PostgreSQL autovacuum fell 11 days behind on an `order_items` table, accumulating 847 million dead tuples and 186 GB of...
og:url: https://daily.dev/posts/postgres-autovacuum-fell-behind-for-11-days-then-one-update-locked-checkout-e5buzagbr
og:image: https://api.daily.dev/og/posts/e5bUzaGBR.png
og:image:alt: Postgres Autovacuum Fell Behind for 11 Days. Then One UPDATE Locked Checkout
og:image:width: 1200
og:image:height: 630
og:locale: en
---

> ## Documentation Index
> Fetch the complete documentation index at: https://daily.dev/llms.txt
> Use this file to discover all available pages before exploring further.

# Postgres Autovacuum Fell Behind for 11 Days. Then One UPDATE Locked Checkout

**[Medium](https://daily.dev/sources/medium_js)** · 8 min read · 15 upvotes · 0 comments

## Summary

A production incident postmortem where PostgreSQL autovacuum fell 11 days behind on an `order_items` table, accumulating 847 million dead tuples and 186 GB of heap bloat. A routine bulk UPDATE during Black Friday prep triggered a 38-minute checkout outage, causing 1,847 failed checkouts and ~$19,200 in abandoned carts. The root causes were default autovacuum scale factors too coarse for high-churn tables, cost throttling causing workers to lose the race, long-running analytics transactions blocking vacuum, and zero monitoring on dead tuple ratios or vacuum lag. Fixes included per-table autovacuum tuning (scale_factor reduced to 0.02), Grafana alerts on dead tuple ratios and last_autovacuum age, mandatory batch windows for bulk updates, and a manual VACUUM that ran 4+ hours and restored the dead ratio from 412:1 to 0.03:1.

## Full article

daily.dev links to this article rather than hosting it. Read it at the original source: <https://blog.stackademic.com/postgres-autovacuum-fell-behind-for-11-days-then-one-update-locked-checkout-c7989207a0b3>

## Questions this post answers

### Why did a simple UPDATE on 41,000 rows take 38 minutes and lock checkout in Postgres?

The update had to scan not just the 41,000 live rows but also 847 million dead tuples that autovacuum had failed to reclaim over 11 days, turning a routine update into a full heap scan while holding row-level locks that blocked checkout inserts. The dead-to-live tuple ratio measured 412:1 on the affected table before the fix.

_Developers debugging mysterious lock contention in postgres can find similarly detailed cases discussed around daily.dev._

### What autovacuum settings should I tune to prevent table bloat on high-update PostgreSQL tables?

Lower the default autovacuum_vacuum_scale_factor (0.2) to something like 0.02 and autovacuum_analyze_scale_factor to 0.01 for hot tables, so vacuum triggers far more often than the default threshold of 400,000 dead tuples on a 2-million-row table. Also watch autovacuum_vacuum_cost_delay, since a 2ms default can let vacuum workers fall behind heavy write workloads.

_Teams tuning postgres autovacuum for production workloads often compare configuration approaches on daily.dev._

### How do I check if PostgreSQL autovacuum is falling behind on a table?

Query pg_stat_user_tables for n_dead_tup, n_live_tup, and last_autovacuum to compute the dead tuple ratio and see when autovacuum last ran successfully; also check pg_stat_progress_vacuum to confirm a vacuum is actually running, since workers can silently stop when blocked by long-running transactions like an 11-hour open analytics query.

_Engineers building vacuum-lag alerting for postgres can track related production debugging stories on daily.dev._

## Similar posts on daily.dev

- [3 Lessons From Nearly Blowing Up a Production Database](https://daily.dev/posts/3-lessons-from-nearly-blowing-up-a-production-database-6ayktn7ub) · Atomic Spin · 0 upvotes · 0 comments
- [My queries to monitor autovacuum](https://daily.dev/posts/my-queries-to-monitor-autovacuum-vscqbjwpm) · CYBERTEC PostgreSQL · 0 upvotes · 0 comments
- [Keeping a Postgres queue healthy — PlanetScale](https://daily.dev/posts/keeping-a-postgres-queue-healthy-planetscale-h5pvjk1vk) · Hacker News · 0 upvotes · 0 comments

---

Tags: [#database](https://daily.dev/tags/database), [#postgresql](https://daily.dev/tags/postgresql)

[View this post on daily.dev](https://daily.dev/posts/postgres-autovacuum-fell-behind-for-11-days-then-one-update-locked-checkout-e5buzagbr)

```json
{"@context":"https://schema.org","@graph":[{"@type":"Organization","@id":"https://daily.dev/#organization","name":"daily.dev","url":"https://daily.dev","logo":{"@type":"ImageObject","url":"https://daily.dev/apple-touch-icon.png","width":180,"height":180},"sameAs":["https://twitter.com/dailydotdev","https://github.com/dailydotdev","https://www.linkedin.com/company/daily-dev-ltd"]},{"@type":"WebSite","@id":"https://daily.dev/#website","url":"https://daily.dev","name":"daily.dev","publisher":{"@id":"https://daily.dev/#organization"},"potentialAction":{"@type":"SearchAction","target":{"@type":"EntryPoint","urlTemplate":"https://daily.dev/search?q={search_term_string}"},"query-input":"required name=search_term_string"}}]}
{"@context":"https://schema.org","@type":"TechArticle","headline":"Postgres Autovacuum Fell Behind for 11 Days. Then One UPDATE Locked Checkout","url":"https://daily.dev/posts/postgres-autovacuum-fell-behind-for-11-days-then-one-update-locked-checkout-e5buzagbr","mainEntityOfPage":{"@type":"WebPage","@id":"https://daily.dev/posts/postgres-autovacuum-fell-behind-for-11-days-then-one-update-locked-checkout-e5buzagbr"},"datePublished":"2026-07-19T19:31:21.074Z","dateModified":"2026-09-13T19:27:55.593Z","description":"A production incident postmortem where PostgreSQL autovacuum fell 11 days behind on an `order_items` table, accumulating 847 million dead tuples and 186 GB of...","image":"https://media.daily.dev/image/upload/s--OHB84bZF--/f_auto/v1722860399/public/Placeholder%2010","thumbnailUrl":"https://media.daily.dev/image/upload/s--OHB84bZF--/f_auto/v1722860399/public/Placeholder%2010","isAccessibleForFree":true,"articleSection":"Medium","inLanguage":"en","publisher":{"@type":"Organization","name":"daily.dev","url":"https://daily.dev","logo":{"@type":"ImageObject","url":"https://daily.dev/apple-touch-icon.png","width":180,"height":180}},"author":{"@type":"Organization","name":"Medium","logo":"https://media.daily.dev/image/upload/t_logo,f_auto/v1/logos/medium","url":"https://daily.dev/sources/medium_js"},"commentCount":0,"discussionUrl":"https://daily.dev/posts/postgres-autovacuum-fell-behind-for-11-days-then-one-update-locked-checkout-e5buzagbr","interactionStatistic":[{"@type":"InteractionCounter","interactionType":{"@type":"LikeAction"},"userInteractionCount":15},{"@type":"InteractionCounter","interactionType":{"@type":"CommentAction"},"userInteractionCount":0}],"keywords":"database,postgresql","timeRequired":"PT8M"}
{"@context":"https://schema.org","@type":"BreadcrumbList","itemListElement":[{"@type":"ListItem","position":1,"name":"Home","item":"https://daily.dev"},{"@type":"ListItem","position":2,"name":"Medium","item":"https://daily.dev/sources/medium_js"},{"@type":"ListItem","position":3,"name":"Postgres Autovacuum Fell Behind for 11 Days. Then One UPDATE Locked Checkout"}]}
{"@context":"https://schema.org","@type":"FAQPage","@id":"https://daily.dev/posts/postgres-autovacuum-fell-behind-for-11-days-then-one-update-locked-checkout-e5buzagbr#faq","mainEntity":[{"@type":"Question","name":"Why did a simple UPDATE on 41,000 rows take 38 minutes and lock checkout in Postgres?","acceptedAnswer":{"@type":"Answer","text":"The update had to scan not just the 41,000 live rows but also 847 million dead tuples that autovacuum had failed to reclaim over 11 days, turning a routine update into a full heap scan while holding row-level locks that blocked checkout inserts. The dead-to-live tuple ratio measured 412:1 on the affected table before the fix. Developers debugging mysterious lock contention in postgres can find similarly detailed cases discussed around daily.dev."}},{"@type":"Question","name":"What autovacuum settings should I tune to prevent table bloat on high-update PostgreSQL tables?","acceptedAnswer":{"@type":"Answer","text":"Lower the default autovacuum_vacuum_scale_factor (0.2) to something like 0.02 and autovacuum_analyze_scale_factor to 0.01 for hot tables, so vacuum triggers far more often than the default threshold of 400,000 dead tuples on a 2-million-row table. Also watch autovacuum_vacuum_cost_delay, since a 2ms default can let vacuum workers fall behind heavy write workloads. Teams tuning postgres autovacuum for production workloads often compare configuration approaches on daily.dev."}},{"@type":"Question","name":"How do I check if PostgreSQL autovacuum is falling behind on a table?","acceptedAnswer":{"@type":"Answer","text":"Query pg_stat_user_tables for n_dead_tup, n_live_tup, and last_autovacuum to compute the dead tuple ratio and see when autovacuum last ran successfully; also check pg_stat_progress_vacuum to confirm a vacuum is actually running, since workers can silently stop when blocked by long-running transactions like an 11-hour open analytics query. Engineers building vacuum-lag alerting for postgres can track related production debugging stories on daily.dev."}}]}
```

