<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>PostgreSQL Operations Guides on Netdata</title><link>https://www.netdata.cloud/guides/postgres/</link><description>Recent content in PostgreSQL Operations Guides on Netdata</description><generator>Hugo</generator><language>en-us</language><atom:link href="https://www.netdata.cloud/guides/postgres/index.xml" rel="self" type="application/rss+xml"/><item><title>How PostgreSQL actually works in production: a mental model for operators</title><link>https://www.netdata.cloud/guides/postgres/how-postgres-works-in-production/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://www.netdata.cloud/guides/postgres/how-postgres-works-in-production/</guid><description>&lt;h1 id="how-postgresql-actually-works-in-production-a-mental-model-for-operators">How PostgreSQL actually works in production: a mental model for operators&lt;/h1>
&lt;p>PostgreSQL is a reliable relational database, but that reliability is not magic. It is the emergent behavior of five concrete subsystems that interact in specific, observable ways: the Write-Ahead Log, Multi-Version Concurrency Control, checkpoints, autovacuum, and the process-per-connection model. If you do not understand how these interact, you will misdiagnose table bloat as a missing index, interpret checkpoint I/O spikes as disk failures, and respond to connection exhaustion by raising &lt;code>max_connections&lt;/code> until the OOM killer arrives.&lt;/p></description></item><item><title>PgBouncer pool exhausted: how to diagnose and fix client waits</title><link>https://www.netdata.cloud/guides/postgres/postgres-pgbouncer-pool-exhausted/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://www.netdata.cloud/guides/postgres/postgres-pgbouncer-pool-exhausted/</guid><description>&lt;h1 id="pgbouncer-pool-exhausted-how-to-diagnose-and-fix-client-waits">PgBouncer pool exhausted: how to diagnose and fix client waits&lt;/h1>
&lt;p>Connection timeouts appear in application logs, but &lt;code>pg_stat_activity&lt;/code> shows plenty of &lt;code>idle&lt;/code> PostgreSQL backends. The bottleneck is usually the connection pooler. When PgBouncer exhausts server connections, clients queue at the pooler instead of reaching PostgreSQL. Sustained queuing raises latency and can cause timeouts before the database sees the query.&lt;/p>
&lt;p>Confirm pool exhaustion, distinguish it from PostgreSQL-side connection limits, and fix the root cause without restarting services.&lt;/p></description></item><item><title>PgBouncer vs Pgpool-II vs Odyssey: choosing a PostgreSQL connection pooler</title><link>https://www.netdata.cloud/guides/postgres/postgres-pgbouncer-vs-pgpool/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://www.netdata.cloud/guides/postgres/postgres-pgbouncer-vs-pgpool/</guid><description>&lt;h1 id="pgbouncer-vs-pgpool-ii-vs-odyssey-choosing-a-postgresql-connection-pooler">PgBouncer vs Pgpool-II vs Odyssey: choosing a PostgreSQL connection pooler&lt;/h1>
&lt;p>PostgreSQL uses one backend process per connection. Past a few hundred concurrent connections, memory overhead and context switching dominate and throughput collapses. A connection pooler becomes architecturally mandatory at scale. PgBouncer, pgpool-II, and Odyssey make different trade-offs around session state, prepared statements, throughput, and operational complexity. The wrong choice leads to session-leak bugs, throughput ceilings, or split-brain failures unrelated to PostgreSQL itself.&lt;/p></description></item><item><title>PostgreSQL ALTER TABLE blocked: zero-downtime DDL patterns</title><link>https://www.netdata.cloud/guides/postgres/postgres-alter-table-blocked/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://www.netdata.cloud/guides/postgres/postgres-alter-table-blocked/</guid><description>&lt;h1 id="postgresql-alter-table-blocked-zero-downtime-ddl-patterns">PostgreSQL ALTER TABLE blocked: zero-downtime DDL patterns&lt;/h1>
&lt;p>An &lt;code>ALTER TABLE&lt;/code> to add a column or change a type hangs. Application queries time out. Connection pools saturate. What looked like a simple schema change becomes a production incident.&lt;/p>
&lt;p>By default, &lt;code>ALTER TABLE&lt;/code> acquires an &lt;code>ACCESS EXCLUSIVE&lt;/code> lock. It conflicts with every other lock mode, including &lt;code>ACCESS SHARE&lt;/code> held by a plain &lt;code>SELECT&lt;/code>. Once the DDL statement queues behind a blocker, every subsequent read and write on that table queues behind the waiting DDL. The table goes offline before the &lt;code>ALTER TABLE&lt;/code> executes any work.&lt;/p></description></item><item><title>PostgreSQL autovacuum blocked by long-running transaction: detection and fix</title><link>https://www.netdata.cloud/guides/postgres/postgres-autovacuum-blocked-by-long-transaction/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://www.netdata.cloud/guides/postgres/postgres-autovacuum-blocked-by-long-transaction/</guid><description>&lt;h1 id="postgresql-autovacuum-blocked-by-long-running-transaction-detection-and-fix">PostgreSQL autovacuum blocked by long-running transaction: detection and fix&lt;/h1>
&lt;p>Autovacuum workers are active, but &lt;code>n_dead_tup&lt;/code> climbs and queries slow down as they scan dead tuples. Table bloat grows because &lt;code>VACUUM&lt;/code> cannot reclaim dead row versions: a long-running transaction, abandoned replication slot, or hot-standby feedback is pinning the &lt;strong>xmin horizon&lt;/strong> cluster-wide. The worker is not broken; it is blocked by an older transaction ID that must remain visible.&lt;/p>
&lt;h2 id="what-this-means">What this means&lt;/h2>
&lt;p>PostgreSQL&amp;rsquo;s MVCC keeps old tuple versions in the table until &lt;code>VACUUM&lt;/code> removes them. &lt;code>VACUUM&lt;/code> cannot remove any tuple that might still be visible to an active transaction. The boundary is the &lt;strong>xmin horizon&lt;/strong>: the oldest transaction ID still active anywhere in the cluster.&lt;/p></description></item><item><title>PostgreSQL autovacuum not running: detection, causes, and fixes</title><link>https://www.netdata.cloud/guides/postgres/postgres-autovacuum-not-running/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://www.netdata.cloud/guides/postgres/postgres-autovacuum-not-running/</guid><description>&lt;h1 id="postgresql-autovacuum-not-running-detection-causes-and-fixes">PostgreSQL autovacuum not running: detection, causes, and fixes&lt;/h1>
&lt;p>Table sizes grow faster than insert rates. Query latency creeps up on UPDATE-heavy workloads. &lt;code>pg_stat_user_tables.n_dead_tup&lt;/code> climbs while &lt;code>last_autovacuum&lt;/code> stays frozen. Autovacuum should clean this up, but it is not. Dead tuple accumulation degrades performance and can eventually trigger transaction-ID-wraparound shutdown. Detect why autovacuum is stalled, find the blocker, and fix it without making things worse.&lt;/p>
&lt;h2 id="what-this-means">What this means&lt;/h2>
&lt;p>Autovacuum is a background subsystem that spawns workers to run &lt;code>VACUUM&lt;/code> and &lt;code>ANALYZE&lt;/code> based on table-level thresholds. When it works, it reclaims dead tuple space, updates the free space map, maintains the visibility map, and freezes old transaction IDs to prevent wraparound. When it stops, effects cascade: table and index bloat grow, sequential scans read more dead pages, the planner chooses worse plans as statistics stale, and database age advances toward the 2-billion-transaction hard limit.&lt;/p></description></item><item><title>PostgreSQL autovacuum tuning: per-table thresholds for high-churn workloads</title><link>https://www.netdata.cloud/guides/postgres/postgres-autovacuum-tuning/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://www.netdata.cloud/guides/postgres/postgres-autovacuum-tuning/</guid><description>&lt;h1 id="postgresql-autovacuum-tuning-per-table-thresholds-for-high-churn-workloads">PostgreSQL autovacuum tuning: per-table thresholds for high-churn workloads&lt;/h1>
&lt;p>Default autovacuum settings target modest OLTP workloads. On a 500-million-row table, the global &lt;code>autovacuum_vacuum_scale_factor&lt;/code> of 0.2 means autovacuum ignores the table until dead tuples exceed twenty percent of the row count. For high-churn tables, that delay lets bloat accumulate, degrades indexes, slows scans, and pushes transaction ID age toward wraparound. PostgreSQL overrides these thresholds per table via storage parameters (reloptions), so a 50 GB hot table can run aggressive settings without saturating workers across a 500 MB reference table. This guide covers calculating overrides, sizing worker memory, and verifying vacuum keeps up without drowning the cluster in background I/O.&lt;/p></description></item><item><title>PostgreSQL backup strategy: pg_dump, pg_basebackup, and pgBackRest compared</title><link>https://www.netdata.cloud/guides/postgres/postgres-backup-strategy/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://www.netdata.cloud/guides/postgres/postgres-backup-strategy/</guid><description>&lt;h1 id="postgresql-backup-strategy-pg_dump-pg_basebackup-and-pgbackrest-compared">PostgreSQL backup strategy: pg_dump, pg_basebackup, and pgBackRest compared&lt;/h1>
&lt;p>Your backup tool determines your RPO, RTO, and whether you can restore to a point in time or only to the backup moment. This guide compares logical dumps via &lt;code>pg_dump&lt;/code>, built-in physical copies via &lt;code>pg_basebackup&lt;/code>, and the third-party tool pgBackRest. PostgreSQL 17 introduced native incremental physical backups. Operators must decide whether built-in capabilities are sufficient or whether to adopt alternatives such as Barman or WAL-G. &lt;!-- TODO: verify pgBackRest maintenance status and claimed April 2026 EOL -->&lt;/p></description></item><item><title>PostgreSQL blocking queries: finding the root blocker in a lock cascade</title><link>https://www.netdata.cloud/guides/postgres/postgres-blocking-queries/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://www.netdata.cloud/guides/postgres/postgres-blocking-queries/</guid><description>&lt;h1 id="postgresql-blocking-queries-finding-the-root-blocker-in-a-lock-cascade">PostgreSQL blocking queries: finding the root blocker in a lock cascade&lt;/h1>
&lt;p>A query that normally finishes in milliseconds is now running for minutes. &lt;code>pg_stat_activity&lt;/code> shows a queue of sessions with &lt;code>wait_event_type = 'Lock'&lt;/code>. You identify one session holding the contested lock, but terminating it does not clear the queue. That session was itself blocked by another, which was blocked by another. Until you find the session at the head of the chain, the cascade continues.&lt;/p></description></item><item><title>PostgreSQL checkpoint storms: detection, causes, and tuning</title><link>https://www.netdata.cloud/guides/postgres/postgres-checkpoint-storms/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://www.netdata.cloud/guides/postgres/postgres-checkpoint-storms/</guid><description>&lt;h1 id="postgresql-checkpoint-storms-detection-causes-and-tuning">PostgreSQL checkpoint storms: detection, causes, and tuning&lt;/h1>
&lt;p>If query latency spikes and TPS drops coincide with your &lt;code>checkpoint_timeout&lt;/code> schedule or follow a large bulk load, you are likely hitting a checkpoint storm. PostgreSQL checkpoints guarantee that dirty buffers are on disk. In a healthy system, timed checkpoints occur at &lt;code>checkpoint_timeout&lt;/code> intervals and the background writer spreads that I/O. A storm happens when WAL generation hits &lt;code>max_wal_size&lt;/code> before the timeout, forcing a requested checkpoint that flushes most dirty buffers at once, saturating disk bandwidth and stalling queries.&lt;/p></description></item><item><title>PostgreSQL connection exhaustion: detection, diagnosis, and prevention</title><link>https://www.netdata.cloud/guides/postgres/postgres-connection-exhaustion/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://www.netdata.cloud/guides/postgres/postgres-connection-exhaustion/</guid><description>&lt;h1 id="postgresql-connection-exhaustion-detection-diagnosis-and-prevention">PostgreSQL connection exhaustion: detection, diagnosis, and prevention&lt;/h1>
&lt;p>Application logs show &lt;code>FATAL: sorry, too many clients already&lt;/code>. Health checks are failing. A rolling deploy just finished, and now the database is rejecting connections. Because PostgreSQL uses one process per connection, every slot consumes memory and scheduler overhead. Once &lt;code>max_connections&lt;/code> is reached, the server refuses new backends entirely. Raising &lt;code>max_connections&lt;/code> without fixing the root cause increases memory pressure and context-switch thrashing. This guide covers how to distinguish a true capacity shortage from a leak or pool misconfiguration, how to recover safely, and how to prevent recurrence.&lt;/p></description></item><item><title>PostgreSQL connection refused: pg_hba, listen_addresses, and TCP diagnosis</title><link>https://www.netdata.cloud/guides/postgres/postgres-connection-refused/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://www.netdata.cloud/guides/postgres/postgres-connection-refused/</guid><description>&lt;h1 id="postgresql-connection-refused-pg_hba-listen_addresses-and-tcp-diagnosis">PostgreSQL connection refused: pg_hba, listen_addresses, and TCP diagnosis&lt;/h1>
&lt;p>An application cannot reach PostgreSQL. The client reports either &amp;ldquo;Connection refused,&amp;rdquo; a hang until timeout, or &amp;ldquo;no pg_hba.conf entry.&amp;rdquo; These three symptoms point to different layers. Mixing them up leads to wasted restarts, overly broad firewall rules, or &lt;code>pg_hba.conf&lt;/code> edits that never take effect. Work through the transport layer first, then the network path, then the authorization layer.&lt;/p>
&lt;p>PostgreSQL defaults to binding only to the loopback interface. A fresh installation or container image rejects remote TCP attempts before &lt;code>pg_hba.conf&lt;/code> is consulted. Changing &lt;code>listen_addresses&lt;/code> requires a restart; changing &lt;code>pg_hba.conf&lt;/code> only requires a reload. The diagnostic sequence is: confirm the listener, confirm the path, then confirm the rule.&lt;/p></description></item><item><title>PostgreSQL dead tuples piling up: why autovacuum can't keep up</title><link>https://www.netdata.cloud/guides/postgres/postgres-dead-tuples-piling-up/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://www.netdata.cloud/guides/postgres/postgres-dead-tuples-piling-up/</guid><description>&lt;h1 id="postgresql-dead-tuples-piling-up-why-autovacuum-cant-keep-up">PostgreSQL dead tuples piling up: why autovacuum can&amp;rsquo;t keep up&lt;/h1>
&lt;p>Table sizes grow faster than insert rates, and queries that scanned thousands of rows last week now scan millions. &lt;code>pg_stat_user_tables&lt;/code> shows &lt;code>n_dead_tup&lt;/code> climbing into the millions while &lt;code>last_autovacuum&lt;/code> is stale or absent. Autovacuum is running somewhere in the cluster, but it is losing the race.&lt;/p>
&lt;p>PostgreSQL creates dead tuples on every &lt;code>UPDATE&lt;/code> and &lt;code>DELETE&lt;/code>. Autovacuum reclaims them only when the dead tuple count crosses a threshold. On large or high-churn tables, the default threshold is far too conservative, and even when vacuum does fire, long-running transactions, worker starvation, or streaming replica feedback can prevent tuple removal. The result is bloat: wasted space, slower scans, and eventually transaction ID wraparound risk.&lt;/p></description></item><item><title>PostgreSQL deadlock detected: how to diagnose and prevent deadlocks</title><link>https://www.netdata.cloud/guides/postgres/postgres-deadlock-detected/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://www.netdata.cloud/guides/postgres/postgres-deadlock-detected/</guid><description>&lt;h1 id="postgresql-deadlock-detected-how-to-diagnose-and-prevent-deadlocks">PostgreSQL deadlock detected: how to diagnose and prevent deadlocks&lt;/h1>
&lt;p>&lt;code>ERROR: deadlock detected&lt;/code> means PostgreSQL aborted one transaction to break a circular wait-for graph. The victim returns SQLSTATE &lt;code>40P01&lt;/code>; the application must retry it. Deadlocks are a safety mechanism, not a bug: they fire when concurrent transactions acquire locks in incompatible orders. Even a few per minute degrade user experience, burn retry budget, and mask deeper contention. This guide shows how to read the deadlock output, find the root cause, and stop the cycle.&lt;/p></description></item><item><title>PostgreSQL disk full: emergency recovery and root cause analysis</title><link>https://www.netdata.cloud/guides/postgres/postgres-disk-full/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://www.netdata.cloud/guides/postgres/postgres-disk-full/</guid><description>&lt;h1 id="postgresql-disk-full-emergency-recovery-and-root-cause-analysis">PostgreSQL disk full: emergency recovery and root cause analysis&lt;/h1>
&lt;p>When &lt;code>df -h&lt;/code> shows 100% utilization on the PostgreSQL data volume, queries fail with &amp;ldquo;could not write to file&amp;rdquo;. If &lt;code>pg_wal&lt;/code> fills, the server enters PANIC and refuses to restart until space is freed. The fastest way to make the incident worse is to delete WAL files from &lt;code>pg_wal&lt;/code> manually. PostgreSQL needs those files for crash recovery; removing them causes data inconsistency that forces a restore from backup. Identify which subsystem is consuming space, reclaim it safely, and fix the root cause before the cycle repeats.&lt;/p></description></item><item><title>PostgreSQL ERROR: could not obtain lock — diagnosis and recovery</title><link>https://www.netdata.cloud/guides/postgres/postgres-lock-not-available/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://www.netdata.cloud/guides/postgres/postgres-lock-not-available/</guid><description>&lt;h1 id="postgresql-error-could-not-obtain-lock--diagnosis-and-recovery">PostgreSQL ERROR: could not obtain lock — diagnosis and recovery&lt;/h1>
&lt;p>&lt;code>ERROR: could not obtain lock on row in relation&lt;/code> and &lt;code>ERROR: canceling statement due to lock timeout&lt;/code> mean a query requested a lock but PostgreSQL refused to wait. The database is not down; a session is holding a resource another transaction needs.&lt;/p>
&lt;p>Three variants produce these errors:&lt;/p>
&lt;ul>
&lt;li>&lt;code>SELECT ... FOR UPDATE NOWAIT&lt;/code> fails immediately if the row is locked.&lt;/li>
&lt;li>A statement that exceeds &lt;code>lock_timeout&lt;/code> fails after waiting.&lt;/li>
&lt;li>DDL such as &lt;code>ALTER TABLE&lt;/code> or &lt;code>CREATE INDEX&lt;/code> requires &lt;code>AccessExclusiveLock&lt;/code> and will wait or fail depending on session configuration.&lt;/li>
&lt;/ul>
&lt;p>These often cascade: one long-running query blocks a schema change, the schema change queues behind it, and subsequent queries queue behind the DDL until the connection pool exhausts.&lt;/p></description></item><item><title>PostgreSQL failover with Patroni: detection, promotion, and rollback</title><link>https://www.netdata.cloud/guides/postgres/postgres-failover-with-patroni/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://www.netdata.cloud/guides/postgres/postgres-failover-with-patroni/</guid><description>&lt;h1 id="postgresql-failover-with-patroni-detection-promotion-and-rollback">PostgreSQL failover with Patroni: detection, promotion, and rollback&lt;/h1>
&lt;p>Patroni automates PostgreSQL failover with machine-enforced lease expiration. An agent on each node maintains a leader lock in a distributed consensus store such as etcd or Consul. When the primary stops renewing that lock, a surviving replica acquires it and promotes itself. Detection to promotion typically completes in seconds rather than the minutes a manual procedure requires.&lt;/p>
&lt;p>Automation does not remove operational risk. A network blip between Patroni and its consensus store can look exactly like a primary death. A promoted replica with unbounded lag can wipe out your RPO. An old primary that restarts outside Patroni&amp;rsquo;s control can create a split brain. Understanding detection, promotion, and rollback mechanics is essential in production.&lt;/p></description></item><item><title>PostgreSQL FATAL: Too Many Connections - Causes &amp; Fixes</title><link>https://www.netdata.cloud/guides/postgres/postgres-too-many-connections/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://www.netdata.cloud/guides/postgres/postgres-too-many-connections/</guid><description>&lt;p>Applications throw &lt;code>FATAL: sorry, too many clients already&lt;/code>. Health checks fail. Retries amplify the problem. The database is not down, but it rejects new traffic.&lt;/p>
&lt;p>&lt;a href="https://www.netdata.cloud/guides/postgres/">PostgreSQL&lt;/a> uses a process-per-connection model. Every backend holds memory and scheduler time even when idle. Raising &lt;code>max_connections&lt;/code> usually deepens the problem because the root cause is why slots are occupied and what those backends are doing.&lt;/p>
&lt;p>This guide shows how to diagnose connection exhaustion, distinguish PostgreSQL saturation from pooler exhaustion, and fix common root causes safely.&lt;/p></description></item><item><title>PostgreSQL frozen XID monitoring: catching wraparound 6 months early</title><link>https://www.netdata.cloud/guides/postgres/postgres-frozen-xid-monitoring/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://www.netdata.cloud/guides/postgres/postgres-frozen-xid-monitoring/</guid><description>&lt;h1 id="postgresql-frozen-xid-monitoring-catching-wraparound-6-months-early">PostgreSQL frozen XID monitoring: catching wraparound 6 months early&lt;/h1>
&lt;p>Transaction ID wraparound is a slow burn, not a sudden crisis. The 32-bit XID counter advances with every transaction. If VACUUM freeze does not keep pace, PostgreSQL will eventually refuse new transactions to prevent data corruption. By the time built-in log warnings fire, you may have only hours of runway left. Monitor &lt;code>age(datfrozenxid)&lt;/code> and &lt;code>age(relfrozenxid)&lt;/code> with tiered thresholds so you act with months of lead time.&lt;/p></description></item><item><title>PostgreSQL fsync=off: why disabling it ruins durability</title><link>https://www.netdata.cloud/guides/postgres/postgres-fsync-disabled/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://www.netdata.cloud/guides/postgres/postgres-fsync-disabled/</guid><description>&lt;h1 id="postgresql-fsyncoff-why-disabling-it-ruins-durability">PostgreSQL fsync=off: why disabling it ruins durability&lt;/h1>
&lt;p>You are reviewing a PostgreSQL instance that crashed during a kernel update and now fails to start with checksum or WAL errors. Or you are benchmarking write throughput and a guide suggests turning off fsync to remove the &amp;ldquo;fsync bottleneck.&amp;rdquo; Maybe you are auditing configuration drift and found &lt;code>fsync = off&lt;/code> in a &lt;code>postgresql.conf&lt;/code> that was copied from an old developer environment.&lt;/p>
&lt;p>PostgreSQL&amp;rsquo;s default is &lt;code>fsync = on&lt;/code> in all supported versions. Disabling it can make bulk loads and heavy write workloads appear dramatically faster because the database stops waiting for the operating system to flush write-ahead log (WAL) records to durable storage. The cost is that an operating system crash or power loss can leave the data directory in an unrecoverable state. WAL is the source of truth; &lt;code>fsync = off&lt;/code> means the database trusts the OS page cache with that truth. When that trust is broken by a power event, the resulting corruption may not be detected until you attempt to start the server or query a specific table.&lt;/p></description></item><item><title>PostgreSQL idle in transaction: detecting and killing zombie sessions</title><link>https://www.netdata.cloud/guides/postgres/postgres-idle-in-transaction/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://www.netdata.cloud/guides/postgres/postgres-idle-in-transaction/</guid><description>&lt;h1 id="postgresql-idle-in-transaction-detecting-and-killing-zombie-sessions">PostgreSQL idle in transaction: detecting and killing zombie sessions&lt;/h1>
&lt;p>When &lt;code>pg_stat_activity&lt;/code> fills with &lt;code>idle in transaction&lt;/code> sessions, autovacuum stalls, dead tuples accumulate, and applications throw &amp;ldquo;too many clients&amp;rdquo; errors while backends sit idle. These zombie sessions hold a transaction snapshot. Under MVCC, this prevents VACUUM from reclaiming dead tuples created after the transaction started, causing table bloat, blocked DDL, and transaction-ID wraparound pressure.&lt;/p>
&lt;p>This guide shows how to detect these sessions, determine whether they are blocking work, terminate them safely, and prevent recurrence.&lt;/p></description></item><item><title>PostgreSQL index bloat: detection and REINDEX CONCURRENTLY recovery</title><link>https://www.netdata.cloud/guides/postgres/postgres-index-bloat/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://www.netdata.cloud/guides/postgres/postgres-index-bloat/</guid><description>&lt;h1 id="postgresql-index-bloat-detection-and-reindex-concurrently-recovery">PostgreSQL index bloat: detection and REINDEX CONCURRENTLY recovery&lt;/h1>
&lt;p>Queries that used to return in milliseconds are now spiking to hundreds. Disk usage grows faster than insert volume. &lt;code>EXPLAIN&lt;/code> shows a bitmap index scan pulling thousands of heap pages for a selective filter. The cause is usually index bloat: dead pages and fragmentation in B-tree indexes. Unlike table bloat, index bloat is not visible in &lt;code>pg_stat_user_tables&lt;/code>, and it often degrades query latency or fills a volume before you notice it. Detect bloat with &lt;code>pgstattuple&lt;/code> and recover online with &lt;code>REINDEX CONCURRENTLY&lt;/code>.&lt;/p></description></item><item><title>PostgreSQL logical replication failures: conflicts, schema drift, and recovery</title><link>https://www.netdata.cloud/guides/postgres/postgres-logical-replication-failures/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://www.netdata.cloud/guides/postgres/postgres-logical-replication-failures/</guid><description>&lt;h1 id="postgresql-logical-replication-failures-conflicts-schema-drift-and-recovery">PostgreSQL logical replication failures: conflicts, schema drift, and recovery&lt;/h1>
&lt;p>Your subscriber is behind. &lt;code>pg_stat_subscription&lt;/code> shows a stalled LSN, the apply worker is throwing errors, or worse: replication appears healthy while the subscriber silently diverges from the publisher. Logical replication does not replicate DDL, does not resolve row conflicts automatically, and will halt on the first integrity error it encounters. When it breaks, the failure is often on the subscriber, but the root cause may be schema drift on either side, a forgotten replication slot on the publisher, or a local write that violated the assumption that the subscriber is read-only.&lt;/p></description></item><item><title>PostgreSQL major version upgrade: pg_upgrade, logical replication, and rollback plans</title><link>https://www.netdata.cloud/guides/postgres/postgres-major-version-upgrade/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://www.netdata.cloud/guides/postgres/postgres-major-version-upgrade/</guid><description>&lt;h1 id="postgresql-major-version-upgrade-pg_upgrade-logical-replication-and-rollback-plans">PostgreSQL major version upgrade: pg_upgrade, logical replication, and rollback plans&lt;/h1>
&lt;p>A major version upgrade is one of the highest-risk maintenance operations on a PostgreSQL cluster. Storage format, system catalogs, and the query planner all change between major versions, so you cannot simply restart the server with new binaries. Every major upgrade is a migration, even when it happens on the same host.&lt;/p>
&lt;p>Most teams choose between two paths. &lt;code>pg_upgrade&lt;/code> rewrites system catalogs while reusing or copying data files. It is fast but requires a downtime window. Logical replication streams row changes to a new cluster running the target version, enabling near-zero-downtime cutover but adding operational complexity. The wrong choice is usually the one made without understanding rollback boundaries.&lt;/p></description></item><item><title>PostgreSQL missing indexes: detection from pg_stat_statements and logs</title><link>https://www.netdata.cloud/guides/postgres/postgres-missing-indexes/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://www.netdata.cloud/guides/postgres/postgres-missing-indexes/</guid><description>&lt;h1 id="postgresql-missing-indexes-detection-from-pg_stat_statements-and-logs">PostgreSQL missing indexes: detection from pg_stat_statements and logs&lt;/h1>
&lt;p>When the planner cannot find a suitable index path, it falls back to a sequential scan. On large tables, this turns millisecond queries into multi second outages.&lt;/p>
&lt;p>PostgreSQL exposes evidence through built-in instrumentation: &lt;code>pg_stat_user_tables&lt;/code> tracks sequential versus index scan ratios, &lt;code>pg_stat_statements&lt;/code> surfaces the most time-consuming query fingerprints, and &lt;code>auto_explain&lt;/code> logs execution plans that reveal &lt;code>Seq Scan&lt;/code> nodes directly.&lt;/p>
&lt;p>This guide gives an operator workflow to detect missing indexes using read-only checks and hypothetical index simulation. It assumes &lt;code>pg_stat_statements&lt;/code> is enabled; if not, adding it to &lt;code>shared_preload_libraries&lt;/code> requires a server restart.&lt;/p></description></item><item><title>PostgreSQL Monitoring Checklist: The Signals Every Production Database Needs</title><link>https://www.netdata.cloud/guides/postgres/postgres-monitoring-checklist/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://www.netdata.cloud/guides/postgres/postgres-monitoring-checklist/</guid><description>&lt;p>&lt;a href="https://www.netdata.cloud/guides/postgres/">PostgreSQL&lt;/a> exposes hundreds of counters across the &lt;code>pg_stat_*&lt;/code> views, yet most production outages trace back to a small set of undetected conditions. Bloat accumulates silently until vacuum cannot catch up. Replication lag grows until failover becomes a data-loss event. Transaction ID age crosses a threshold and the database stops accepting writes. The problem is rarely a lack of metrics. It is knowing which signals to instrument at each stage of operational maturity.&lt;/p></description></item><item><title>PostgreSQL monitoring maturity model: from reactive to self-healing</title><link>https://www.netdata.cloud/guides/postgres/postgres-monitoring-maturity-model/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://www.netdata.cloud/guides/postgres/postgres-monitoring-maturity-model/</guid><description>&lt;h1 id="postgresql-monitoring-maturity-model-from-reactive-to-self-healing">PostgreSQL monitoring maturity model: from reactive to self-healing&lt;/h1>
&lt;p>Production PostgreSQL does not usually fail catastrophically; it drifts. An unwatched dashboard, an untested backup, a regressing query plan, or a filling replication slot slowly creates an incident. This model gives you eight observable stages to benchmark your operations, with measurable indicators and common stuck points from production runbooks. Use it to find your current stage, the next transition enabler, and the organizational traps that cause regression.&lt;/p></description></item><item><title>PostgreSQL out of memory: OOM killer, shared_buffers, and work_mem</title><link>https://www.netdata.cloud/guides/postgres/postgres-out-of-memory/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://www.netdata.cloud/guides/postgres/postgres-out-of-memory/</guid><description>&lt;h1 id="postgresql-out-of-memory-oom-killer-shared_buffers-and-work_mem">PostgreSQL out of memory: OOM killer, shared_buffers, and work_mem&lt;/h1>
&lt;p>Your PostgreSQL primary restarts without warning, or individual backends vanish from the process list. The kernel log shows &lt;code>Out of Memory: Killed process 12345 (postgres)&lt;/code>. Existing connections may survive, but new connections fail until the postmaster recovers. This is a memory accounting mismatch between Linux overcommit, PostgreSQL shared and private memory allocation, and how you size &lt;code>shared_buffers&lt;/code> and &lt;code>work_mem&lt;/code>.&lt;/p>
&lt;p>Inside Kubernetes or containers, the symptom is identical but the mechanism differs: cgroup v2 &lt;code>memory.max&lt;/code> triggers an immediate SIGKILL with no ENOMEM grace period. In both cases, stop guessing at memory limits and start budgeting.&lt;/p></description></item><item><title>PostgreSQL pg_upgrade failures: extension, collation, and locale gotchas</title><link>https://www.netdata.cloud/guides/postgres/postgres-pg-upgrade-failures/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://www.netdata.cloud/guides/postgres/postgres-pg-upgrade-failures/</guid><description>&lt;h1 id="postgresql-pg_upgrade-failures-extension-collation-and-locale-gotchas">PostgreSQL pg_upgrade failures: extension, collation, and locale gotchas&lt;/h1>
&lt;p>You ran &lt;code>pg_upgrade --check&lt;/code> and it reported a collation version mismatch. Or the upgrade finished, your application reconnected, and PostGIS functions failed with &lt;code>could not access file '$libdir/postgis-3'&lt;/code>. Maybe &lt;code>SELECT&lt;/code> queries against text columns returned rows in the wrong order, or unique indexes threw violations against previously clean data. These are structural gaps between what pg_upgrade copies (catalog metadata) and what it does not verify (shared libraries, OS collation rules, and runtime configuration).&lt;/p></description></item><item><title>PostgreSQL pg_wal directory full: causes and emergency recovery</title><link>https://www.netdata.cloud/guides/postgres/postgres-wal-disk-full/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://www.netdata.cloud/guides/postgres/postgres-wal-disk-full/</guid><description>&lt;h1 id="postgresql-pg_wal-directory-full-causes-and-emergency-recovery">PostgreSQL pg_wal directory full: causes and emergency recovery&lt;/h1>
&lt;p>Your paging system fires because the PostgreSQL primary has stopped accepting writes. The error log reports a disk-full condition, and the WAL volume is at 100%. You cannot simply delete files from pg_wal to free space: doing so corrupts the database and breaks replication.&lt;/p>
&lt;p>PostgreSQL recycles WAL segments only after a checkpoint, and only when they are no longer needed for crash recovery, archiving, or replication slots. Archiving failures, stalled replication slots, or bulk loads that exceed max_wal_size cause unbounded accumulation. This guide covers identification, safe recovery, and prevention.&lt;/p></description></item><item><title>PostgreSQL prepared statement plan cache: generic vs custom plan pitfalls</title><link>https://www.netdata.cloud/guides/postgres/postgres-prepared-statement-plan-cache/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://www.netdata.cloud/guides/postgres/postgres-prepared-statement-plan-cache/</guid><description>&lt;h1 id="postgresql-prepared-statement-plan-cache-generic-vs-custom-plan-pitfalls">PostgreSQL prepared statement plan cache: generic vs custom plan pitfalls&lt;/h1>
&lt;p>When an application repeats the same query with different parameters, prepared statements avoid repeated parsing. But PostgreSQL&amp;rsquo;s plan cache does not work the way many operators expect. The server does not automatically cache the first plan it generates. It executes the first five invocations with custom plans that see the actual bound values. At the sixth execution, the planner compares the average cost of those custom plans against a generic plan built with placeholders. If the generic plan looks cheaper, the session switches to it permanently. There is no automatic reversion.&lt;/p></description></item><item><title>PostgreSQL replica disconnected: detecting and recovering streaming replication</title><link>https://www.netdata.cloud/guides/postgres/postgres-replica-disconnected/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://www.netdata.cloud/guides/postgres/postgres-replica-disconnected/</guid><description>&lt;h1 id="postgresql-replica-disconnected-detecting-and-recovering-streaming-replication">PostgreSQL replica disconnected: detecting and recovering streaming replication&lt;/h1>
&lt;p>When &lt;code>replay_lag&lt;/code> grows and the primary&amp;rsquo;s &lt;code>pg_stat_replication&lt;/code> no longer lists the standby, queries return stale data and failover is unsafe. PostgreSQL&amp;rsquo;s WAL receiver automatically retries when streaming breaks, cycling through WAL archive, local &lt;code>pg_wal&lt;/code>, and streaming connections. That loop can mask the root cause while the replica drifts toward an unrecoverable gap. Determine whether the replica is temporarily stalled, beyond recovery, or missing its replication slot on the primary.&lt;/p></description></item><item><title>PostgreSQL replica out of sync: timeline mismatches and recovery</title><link>https://www.netdata.cloud/guides/postgres/postgres-replica-out-of-sync/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://www.netdata.cloud/guides/postgres/postgres-replica-out-of-sync/</guid><description>&lt;h1 id="postgresql-replica-out-of-sync-timeline-mismatches-and-recovery">PostgreSQL replica out of sync: timeline mismatches and recovery&lt;/h1>
&lt;p>A PostgreSQL streaming replica that was healthy yesterday now refuses to start with a timeline mismatch error, or a former primary that you brought back online cannot rejoin the cluster as a standby. The log shows &lt;code>requested timeline N is not a child of this server's history&lt;/code> and the replica loops in crash recovery while the current primary continues to diverge. This guide covers identifying the divergence point, choosing between &lt;code>pg_rewind&lt;/code> and a full re-clone, and recovering the replica without introducing split-brain or data loss.&lt;/p></description></item><item><title>PostgreSQL replication lag: detection, diagnosis, and fixes</title><link>https://www.netdata.cloud/guides/postgres/postgres-replication-lag/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://www.netdata.cloud/guides/postgres/postgres-replication-lag/</guid><description>&lt;h1 id="postgresql-replication-lag-detection-diagnosis-and-fixes">PostgreSQL replication lag: detection, diagnosis, and fixes&lt;/h1>
&lt;p>Replication lag is the distance between the last WAL record generated on the primary and the last record applied on a replica. In asynchronous streaming replication, a few seconds of lag is normal. When lag grows without bound, your recovery point objective becomes fiction.&lt;/p>
&lt;p>Lag often grows silently. Replication processes stay connected, WAL streams flow, and uptime checks stay green while the byte gap creeps from megabytes to gigabytes. Promoting a replica that is hours behind destroys the consistency your application assumes.&lt;/p></description></item><item><title>PostgreSQL replication slot bloat: when a stale slot fills the disk</title><link>https://www.netdata.cloud/guides/postgres/postgres-replication-slot-bloat/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://www.netdata.cloud/guides/postgres/postgres-replication-slot-bloat/</guid><description>&lt;h1 id="postgresql-replication-slot-bloat-when-a-stale-slot-fills-the-disk">PostgreSQL replication slot bloat: when a stale slot fills the disk&lt;/h1>
&lt;p>Disk is filling on the primary. Table sizes are stable and active replicas show healthy replication lag, but WAL in pg_wal keeps growing and is not being recycled. The most likely cause is a stale replication slot. When a logical subscriber, CDC connector, or physical replica disconnects without cleaning up its slot, PostgreSQL retains every WAL segment from the slot&amp;rsquo;s restart_lsn onward. This retention ignores max_wal_size unless max_slot_wal_keep_size is set to a finite value. The default is -1 (unlimited). The primary will retain WAL until the disk fills and writes halt. This guide covers confirmation, recovery, and prevention.&lt;/p></description></item><item><title>PostgreSQL row-level lock contention: SELECT FOR UPDATE patterns and fixes</title><link>https://www.netdata.cloud/guides/postgres/postgres-row-level-lock-contention/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://www.netdata.cloud/guides/postgres/postgres-row-level-lock-contention/</guid><description>&lt;h1 id="postgresql-row-level-lock-contention-select-for-update-patterns-and-fixes">PostgreSQL row-level lock contention: SELECT FOR UPDATE patterns and fixes&lt;/h1>
&lt;p>P99 latency doubles. &lt;code>pg_stat_activity&lt;/code> shows &lt;code>wait_event_type = 'Lock'&lt;/code>. Queries are simple, indexes are present, and CPU is idle. The culprit is usually a row-level lock queue: one transaction holds a tuple lock longer than expected, and every subsequent transaction touching the same row waits in FIFO order.&lt;/p>
&lt;p>This is not a deadlock. PostgreSQL detects deadlocks automatically and kills one participant. Lock contention is different: a slow or abandoned transaction acts as a head-of-line blocker. Unless you are polling &lt;code>pg_locks&lt;/code> and transaction age, the queue is invisible in query logs. The pattern is most common with &lt;code>SELECT ... FOR UPDATE&lt;/code> in database-backed job queues, &lt;code>UPDATE&lt;/code> statements that silently take stronger locks than intended, and session-level advisory locks that survive rollback.&lt;/p></description></item><item><title>PostgreSQL sequential scan on a large table: when to add an index and when not to</title><link>https://www.netdata.cloud/guides/postgres/postgres-sequential-scan-on-large-table/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://www.netdata.cloud/guides/postgres/postgres-sequential-scan-on-large-table/</guid><description>&lt;h1 id="postgresql-sequential-scan-on-a-large-table-when-to-add-an-index-and-when-not-to">PostgreSQL sequential scan on a large table: when to add an index and when not to&lt;/h1>
&lt;p>Seeing &lt;code>Seq Scan&lt;/code> on a large table in &lt;code>EXPLAIN&lt;/code> does not mean the planner is wrong. Adding a B-tree index often makes the query slower, burdens every write path with index maintenance, and wastes storage. PostgreSQL&amp;rsquo;s planner compares the estimated cost of reading the table sequentially against the estimated cost of traversing an index and fetching rows from the heap. There is no hardcoded row-percentage threshold. On SSD-backed servers, the default cost parameters are frequently stale, so the planner may be wrong in either direction.&lt;/p></description></item><item><title>PostgreSQL shared_buffers tuning: 25% of RAM and why that rule of thumb breaks</title><link>https://www.netdata.cloud/guides/postgres/postgres-shared-buffers-tuning/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://www.netdata.cloud/guides/postgres/postgres-shared-buffers-tuning/</guid><description>&lt;h1 id="postgresql-shared_buffers-tuning-25-of-ram-and-why-that-rule-of-thumb-breaks">PostgreSQL shared_buffers tuning: 25% of RAM and why that rule of thumb breaks&lt;/h1>
&lt;p>The PostgreSQL documentation suggests 25% of RAM as a starting value for shared_buffers on dedicated servers and warns that values above 40% rarely improve performance. In production, operators routinely use 25% as a default and then encounter checkpoint storms, OOM kills, or degraded analytical query performance. The rule breaks because PostgreSQL does not coordinate with the Linux kernel page cache. The same page can exist in both shared_buffers and the OS page cache, so an oversized PostgreSQL cache starves the kernel of memory needed for sequential scans, WAL, and temporary files.&lt;/p></description></item><item><title>PostgreSQL Slow Queries: Diagnosis From Log To Plan To Fix</title><link>https://www.netdata.cloud/guides/postgres/postgres-slow-queries-diagnosis/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://www.netdata.cloud/guides/postgres/postgres-slow-queries-diagnosis/</guid><description>&lt;p>A query that returned in 10 ms yesterday is now taking 8 seconds. No deploys, no schema changes. Before adding an index or restarting the database, determine whether the slowness is in the plan, the data, or the environment. This guide covers a three-layer workflow: log-based discovery with &lt;code>log_min_duration_statement&lt;/code>, aggregate profiling with &lt;code>pg_stat_statements&lt;/code>, and per-query execution plan capture with &lt;code>auto_explain&lt;/code> and manual &lt;code>EXPLAIN (ANALYZE, BUFFERS)&lt;/code>.&lt;/p>

&lt;pre class="mermaid" data-source="markdown-fence">flowchart TD
 A[Slow query reported] --> B[Check pg_stat_statements for mean_time and stddev_time]
 B --> C{High stddev relative to mean?}
 C -->|Yes| D[Plan flapping or parameter skew]
 C -->|No| E[Stable plan or system bottleneck]
 D --> F[Capture plan with auto_explain or EXPLAIN]
 E --> F
 F --> G{Estimated rows far from actual?}
 G -->|Yes| H[Stale statistics or skewed data]
 G -->|No| I[Bloat, locks, or cache pressure]&lt;/pre>
&lt;h2 id="what-this-means">What This Means&lt;/h2>
&lt;p>A slow query is a symptom. &lt;a href="https://www.netdata.cloud/guides/postgres/">PostgreSQL&amp;rsquo;s planner&lt;/a> chooses a path based on statistics from &lt;code>ANALYZE&lt;/code>, bound parameter values, and configuration such as &lt;code>work_mem&lt;/code>. When these inputs change, the same query text can switch from a hash join to a nested loop and slow down by orders of magnitude.&lt;/p></description></item><item><title>PostgreSQL split-brain after failover: detection and reconciliation</title><link>https://www.netdata.cloud/guides/postgres/postgres-split-brain-after-failover/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://www.netdata.cloud/guides/postgres/postgres-split-brain-after-failover/</guid><description>&lt;h1 id="postgresql-split-brain-after-failover-detection-and-reconciliation">PostgreSQL split-brain after failover: detection and reconciliation&lt;/h1>
&lt;p>A failover should leave one primary and a clean topology. Split-brain means two instances accept writes, application connections are split across both nodes, and transaction histories diverge. It typically starts with a network partition, a missed demotion signal, or a health-check false positive that promotes a replica while the old primary keeps running. Once both primaries accept transactions, timelines diverge and you must reconcile.&lt;/p></description></item><item><title>PostgreSQL statistics out of date: ANALYZE, default_statistics_target, and bad plans</title><link>https://www.netdata.cloud/guides/postgres/postgres-statistics-out-of-date/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://www.netdata.cloud/guides/postgres/postgres-statistics-out-of-date/</guid><description>&lt;h1 id="postgresql-statistics-out-of-date-analyze-default_statistics_target-and-bad-plans">PostgreSQL statistics out of date: ANALYZE, default_statistics_target, and bad plans&lt;/h1>
&lt;p>When &lt;code>EXPLAIN&lt;/code> shows a nested loop joining a million-row table, or a sequential scan where an index should win, and the query text has not changed, stale planner statistics are the likely culprit.&lt;/p>
&lt;p>PostgreSQL&amp;rsquo;s cost-based optimizer relies on &lt;code>ANALYZE&lt;/code>-derived catalog data to estimate cardinality. &lt;code>ANALYZE&lt;/code> samples table contents and writes histograms, most-common-value lists, and correlation data into &lt;code>pg_statistic&lt;/code>. When that data no longer matches the on-disk distribution, the planner misestimates row counts. A cardinality underestimate favors a nested loop; an overestimate favors a sequential scan. The result is a sudden latency spike that connection pooling or hardware scaling will not fix.&lt;/p></description></item><item><title>PostgreSQL streaming replication broken: how to rebuild without full basebackup</title><link>https://www.netdata.cloud/guides/postgres/postgres-streaming-replication-broken/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://www.netdata.cloud/guides/postgres/postgres-streaming-replication-broken/</guid><description>&lt;h1 id="postgresql-streaming-replication-broken-how-to-rebuild-without-full-basebackup">PostgreSQL streaming replication broken: how to rebuild without full basebackup&lt;/h1>
&lt;p>When a replica logs &lt;code>requested WAL segment has already been removed&lt;/code>, or after failover when the old primary must rejoin as a standby, a full &lt;code>pg_basebackup&lt;/code> on a multi-terabyte database can take hours and saturate network and disk. If data files have diverged but are mostly identical, &lt;code>pg_rewind&lt;/code> can resync by copying only changed blocks. It is faster than a base backup, but has strict prerequisites. If &lt;code>pg_rewind&lt;/code> crashes mid-operation, the target data directory is likely corrupt and only a fresh base backup is safe.&lt;/p></description></item><item><title>PostgreSQL synchronous_commit: durability vs throughput trade-offs</title><link>https://www.netdata.cloud/guides/postgres/postgres-synchronous-commit-tuning/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://www.netdata.cloud/guides/postgres/postgres-synchronous-commit-tuning/</guid><description>&lt;h1 id="postgresql-synchronous_commit-durability-vs-throughput-trade-offs">PostgreSQL synchronous_commit: durability vs throughput trade-offs&lt;/h1>
&lt;p>&lt;code>synchronous_commit&lt;/code> moves the durability boundary between a client receiving COMMIT OK and the data actually surviving a crash. The default, &lt;code>on&lt;/code>, flushes local WAL to disk. With synchronous replication enabled, it also waits for a standby to flush. The five values (&lt;code>off&lt;/code>, &lt;code>local&lt;/code>, &lt;code>on&lt;/code>, &lt;code>remote_write&lt;/code>, and &lt;code>remote_apply&lt;/code>) trade latency against survival guarantees. The wrong choice either accepts unplanned data loss or strangles write throughput with network round-trips.&lt;/p></description></item><item><title>PostgreSQL table bloat: detection, measurement, and remediation</title><link>https://www.netdata.cloud/guides/postgres/postgres-table-bloat/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://www.netdata.cloud/guides/postgres/postgres-table-bloat/</guid><description>&lt;h1 id="postgresql-table-bloat-detection-measurement-and-remediation">PostgreSQL table bloat: detection, measurement, and remediation&lt;/h1>
&lt;p>Table bloat appears as tables growing while row counts stay flat, sequential scans slowing despite indexes, and disk alarms that do not track business growth. PostgreSQL&amp;rsquo;s MVCC writes new tuple versions instead of overwriting old ones; every UPDATE leaves a dead row and every DELETE leaves invisible garbage. VACUUM reclaims that space for reuse within the file, but plain VACUUM does not shrink the relation on disk. When dead tuples outpace cleanup, a table can become several times larger than its logical content.&lt;/p></description></item><item><title>PostgreSQL tablespace management: moving data without downtime</title><link>https://www.netdata.cloud/guides/postgres/postgres-tablespace-management/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://www.netdata.cloud/guides/postgres/postgres-tablespace-management/</guid><description>&lt;h1 id="postgresql-tablespace-management-moving-data-without-downtime">PostgreSQL tablespace management: moving data without downtime&lt;/h1>
&lt;p>PostgreSQL tablespaces map relations to specific filesystem paths. Moving data between tablespaces is not an online operation in core PostgreSQL: &lt;code>ALTER TABLE SET TABLESPACE&lt;/code> copies the relation&amp;rsquo;s data files while holding an &lt;code>ACCESS EXCLUSIVE&lt;/code> lock for the entire duration. For a large relation, reads and writes block for minutes or hours. This guide covers three operational paths: the built-in offline move for small or maintenance-tolerant relations, offline symlink relocation for entire tablespace directories, and near-zero-downtime workarounds for heavy tables.&lt;/p></description></item><item><title>PostgreSQL temp file explosion: work_mem tuning and disk pressure</title><link>https://www.netdata.cloud/guides/postgres/postgres-temp-file-explosion/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://www.netdata.cloud/guides/postgres/postgres-temp-file-explosion/</guid><description>&lt;h1 id="postgresql-temp-file-explosion-work_mem-tuning-and-disk-pressure">PostgreSQL temp file explosion: work_mem tuning and disk pressure&lt;/h1>
&lt;p>Disk usage on your PostgreSQL primary is climbing by gigabytes per hour. Query latency has doubled, &lt;code>iowait&lt;/code> is spiking, and you have not deployed new code. The database is writing large temporary files to disk. In-memory operations such as sorts, hash joins, and materialization are spilling because they exceed the per-operation memory budget. The root cause is almost always a mismatch between &lt;code>work_mem&lt;/code> and actual query memory demand, compounded by the fact that PostgreSQL applies this limit per plan node, not per query or session.&lt;/p></description></item><item><title>PostgreSQL TOAST table bloat: detection and recovery for large columns</title><link>https://www.netdata.cloud/guides/postgres/postgres-toast-table-bloat/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://www.netdata.cloud/guides/postgres/postgres-toast-table-bloat/</guid><description>&lt;h1 id="postgresql-toast-table-bloat-detection-and-recovery-for-large-columns">PostgreSQL TOAST table bloat: detection and recovery for large columns&lt;/h1>
&lt;p>Unexplained disk growth on a PostgreSQL instance often traces to TOAST bloat. A table with large JSONB, text, or bytea columns grows far beyond live data size, and queries that materialize those columns slow down. A plain VACUUM does not shrink the on-disk footprint because the dead tuples are in the companion pg_toast table, not the main heap.&lt;/p>
&lt;p>TOAST (The Oversized-Attribute Storage Technique) moves values exceeding the TOAST_TUPLE_TARGET out of the main row and into a companion pg_toast.NNNN table. PostgreSQL compresses the value and splits it into roughly 2 KB chunks, each stored as a separate row with a unique index on (chunk_id, chunk_seq). Updates or deletes to the row leave dead chunks in the TOAST table. Because operators often monitor only the parent table, TOAST bloat grows silently until it dominates disk usage.&lt;/p></description></item><item><title>PostgreSQL transaction ID wraparound: detection and emergency recovery</title><link>https://www.netdata.cloud/guides/postgres/postgres-transaction-id-wraparound/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://www.netdata.cloud/guides/postgres/postgres-transaction-id-wraparound/</guid><description>&lt;h1 id="postgresql-transaction-id-wraparound-detection-and-emergency-recovery">PostgreSQL transaction ID wraparound: detection and emergency recovery&lt;/h1>
&lt;p>PostgreSQL can suddenly stop accepting writes and emit warnings that the database must be vacuumed within a shrinking number of transactions. This is transaction ID wraparound. It is not gradual performance degradation; it is a hard stop that can take a database offline for hours if old tuples are not frozen in time.&lt;/p>
&lt;p>Every write transaction consumes a 32-bit XID. After roughly two billion transactions, the counter nears the point where older tuples could appear to belong to the future, corrupting visibility. PostgreSQL refuses to hand out new XIDs once fewer than roughly three million remain. Warnings appear at roughly forty million remaining. The defense is VACUUM freeze, which marks old rows with FrozenTransactionId so they no longer depend on the live counter.&lt;/p></description></item><item><title>PostgreSQL VACUUM FULL vs pg_repack: choosing the right bloat fix</title><link>https://www.netdata.cloud/guides/postgres/postgres-vacuum-full-vs-pg-repack/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://www.netdata.cloud/guides/postgres/postgres-vacuum-full-vs-pg-repack/</guid><description>&lt;h1 id="postgresql-vacuum-full-vs-pg_repack-choosing-the-right-bloat-fix">PostgreSQL VACUUM FULL vs pg_repack: choosing the right bloat fix&lt;/h1>
&lt;p>Table bloat is the gap between logical data size and on-disk consumption. Heavy UPDATE and DELETE activity leaves dead tuples in the heap. Autovacuum reclaims those tuples for reuse but does not shrink the underlying file. Once sequential scans slow and disk space vanishes, operators must choose between VACUUM FULL and pg_repack. The wrong choice locks a table for hours, exhausts disk mid-operation, or leaves orphaned triggers behind. This guide explains how each tool rewrites the heap, what locks it takes, and how to pick the right one.&lt;/p></description></item><item><title>PostgreSQL WAL archive failures: archive_command exit codes and recovery</title><link>https://www.netdata.cloud/guides/postgres/postgres-wal-archive-failures/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://www.netdata.cloud/guides/postgres/postgres-wal-archive-failures/</guid><description>&lt;h1 id="postgresql-wal-archive-failures-archive_command-exit-codes-and-recovery">PostgreSQL WAL archive failures: archive_command exit codes and recovery&lt;/h1>
&lt;p>WAL archiving is the durability bridge between your PostgreSQL primary and your ability to recover to any point in time. When &lt;code>archive_command&lt;/code> fails, WAL segments stay pinned in &lt;code>pg_wal/&lt;/code>. The directory grows until the filesystem fills, at which point PostgreSQL performs an emergency PANIC shutdown. Even before that happens, every failed segment is a gap in your backup chain, rendering base backups useless for PITR beyond the first missing file.&lt;/p></description></item><item><title>PostgreSQL: checkpoints are occurring too frequently -- what to tune</title><link>https://www.netdata.cloud/guides/postgres/postgres-checkpoints-occurring-too-frequently/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://www.netdata.cloud/guides/postgres/postgres-checkpoints-occurring-too-frequently/</guid><description>&lt;h1 id="postgresql-checkpoints-are-occurring-too-frequently----what-to-tune">PostgreSQL: checkpoints are occurring too frequently &amp;ndash; what to tune&lt;/h1>
&lt;p>Your PostgreSQL logs show &lt;code>LOG: checkpoints are occurring too frequently (9 seconds apart)&lt;/code> with the hint &lt;code>Consider increasing the configuration parameter 'max_wal_size'&lt;/code>. Sustained I/O latency spikes correlate with WAL segment rotation. On write-heavy primaries, this almost always means &lt;code>max_wal_size&lt;/code> is too small for the workload.&lt;/p>
&lt;p>A checkpoint flushes all dirty shared buffers to disk. By default, a checkpoint fires every 5 minutes (&lt;code>checkpoint_timeout&lt;/code>) or every 1 GB of WAL (&lt;code>max_wal_size&lt;/code>), whichever comes first. On a busy OLTP primary, 1 GB of WAL can accumulate in minutes. When &lt;code>max_wal_size&lt;/code> triggers the checkpoint, you get a forced (requested) checkpoint. These are unpredictable, collide with existing write load, and cause visible latency spikes.&lt;/p></description></item><item><title>PostgreSQL: database is not accepting commands to avoid wraparound data loss</title><link>https://www.netdata.cloud/guides/postgres/postgres-database-not-accepting-commands/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://www.netdata.cloud/guides/postgres/postgres-database-not-accepting-commands/</guid><description>&lt;h1 id="postgresql-database-is-not-accepting-commands-to-avoid-wraparound-data-loss">PostgreSQL: database is not accepting commands to avoid wraparound data loss&lt;/h1>
&lt;p>Writes fail abruptly. SELECT still works, but INSERT, UPDATE, DELETE, and DDL return an error like:&lt;/p>
&lt;pre tabindex="0">&lt;code>ERROR: database is not accepting commands that assign new XIDs to avoid wraparound data loss in database &amp;#34;...&amp;#34;
&lt;/code>&lt;/pre>&lt;p>This is PostgreSQL&amp;rsquo;s emergency brake, not a crash. The engine has stopped issuing new transaction IDs to prevent tuple visibility corruption. Modern PostgreSQL lets you recover without restarting or entering single-user mode. Remove whatever is blocking VACUUM progress, then freeze old rows.&lt;/p></description></item><item><title>Reading EXPLAIN ANALYZE: the operator's guide to PostgreSQL query plans</title><link>https://www.netdata.cloud/guides/postgres/postgres-explain-analyze-reading/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://www.netdata.cloud/guides/postgres/postgres-explain-analyze-reading/</guid><description>&lt;h1 id="reading-explain-analyze-the-operators-guide-to-postgresql-query-plans">Reading EXPLAIN ANALYZE: the operator&amp;rsquo;s guide to PostgreSQL query plans&lt;/h1>
&lt;p>When &lt;code>pg_stat_statements&lt;/code> flags a query as a top consumer, &lt;code>EXPLAIN ANALYZE&lt;/code> is the operator&amp;rsquo;s ground truth. It shows what the executor did, node by node, buffer by buffer. Misreading the output leads to useless indexes and production changes that make performance worse.&lt;/p>
&lt;p>This guide covers the mechanics that matter in production: how actual time accumulates through the node tree, why estimated rows diverge from reality, when buffer counts reveal cache misses versus disk reads, and how artifacts like the loops multiplier hide expensive nodes. It is a field manual for deciding, in the next five minutes, whether the problem is a missing index, stale statistics, a bad plan choice, or something deeper.&lt;/p></description></item></channel></rss>