The release of
PostgreSQL 18 introduces one of the most consequential rounds of configuration changes the project has shipped in years. For database engineers and site reliability teams, understanding
PostgreSQL 18 performance tuning is no longer optional—it is the difference between a database that scales predictably and one that buckles under peak load. This guide from MinervaDB presents a complete configuration matrix of the new and changed parameters that govern PostgreSQL 18 performance, along with production-tested tuning recommendations you can apply today.
Whether you are migrating from PostgreSQL 16 or 17, or standing up a greenfield deployment, the parameters below directly influence throughput, latency, and resource efficiency. We have organized them into a practical matrix so you can quickly identify what changed, why it matters, and how MinervaDB recommends configuring each setting for high-performance workloads.
Why PostgreSQL 18 Performance Tuning Matters
Every major PostgreSQL release rebalances the trade-offs between memory, I/O, and CPU. PostgreSQL 18 continues this trend with asynchronous I/O support, refined vacuum behavior, smarter query planning, and improved parallelism. The default values ship conservatively so that PostgreSQL runs on modest hardware, but production systems rarely resemble the defaults. Leaving parameters untuned in PostgreSQL 18 typically leaves 30 to 60 percent of your hardware capacity on the table.
At MinervaDB, we have benchmarked PostgreSQL 18 across OLTP, analytical, and mixed workloads. The recurring lesson is simple: the new configuration parameters reward operators who understand the underlying subsystem. A single misconfigured value—such as an oversized
work_mem combined with high concurrency—can trigger out-of-memory events that cascade into downtime. The matrix approach below helps you reason about each parameter in isolation and in combination.
The PostgreSQL 18 Performance Configuration Matrix
The following configuration matrix summarizes the parameters most relevant to PostgreSQL 18 performance. For each parameter we list the subsystem it affects, the default value, and the MinervaDB recommended starting point for a well-provisioned production server. Treat these as informed baselines, then validate against your own metrics.
| Parameter |
Subsystem |
PG18 Default |
MinervaDB Recommendation |
io_method |
Asynchronous I/O |
worker |
io_uring on modern Linux |
io_workers |
Asynchronous I/O |
3 |
4–8 for high-throughput storage |
effective_io_concurrency |
I/O |
16 |
128–256 for NVMe SSD |
maintenance_io_concurrency |
I/O |
16 |
64–128 for NVMe SSD |
shared_buffers |
Memory |
128MB |
25% of RAM |
work_mem |
Memory |
4MB |
16–64MB, capped by concurrency |
vacuum_max_eager_freeze_failure_rate |
Vacuum |
0.03 |
0.03 (raise cautiously) |
autovacuum_worker_slots |
Vacuum |
16 |
16–32 for busy clusters |
autovacuum_vacuum_max_threshold |
Vacuum |
100000000 |
Tune per table churn |
max_parallel_workers_per_gather |
Parallelism |
2 |
4–8 for analytics |
track_cost_delay_timing |
Observability |
off |
on during tuning windows |
enable_self_join_elimination |
Planner |
on |
on |
Asynchronous I/O: The Headline PostgreSQL 18 Performance Feature
The most significant PostgreSQL 18 performance change is the introduction of a true asynchronous I/O subsystem, as detailed in the
PostgreSQL 18 release notes. Historically, PostgreSQL relied on synchronous read calls and operating-system readahead to hide storage latency. PostgreSQL 18 adds a configurable I/O method controlled by the new
io_method parameter (see the
official PostgreSQL 18 documentation), with support for
io_uring on Linux, a dedicated worker pool, and a synchronous fallback.
Configuring io_method and io_workers
On modern Linux kernels (5.1 and later, ideally 6.x), MinervaDB recommends setting
io_method = io_uring. This lets PostgreSQL submit I/O requests to the kernel without blocking backend processes, dramatically improving sequential scans, bitmap heap scans, and vacuum throughput. If
io_uring is unavailable or restricted by your security policy, fall back to
io_method = worker and increase
io_workers from the default of 3 to somewhere between 4 and 8, matching the parallelism your storage can absorb.
In our internal benchmarks, enabling
io_uring on NVMe storage reduced large sequential scan times by 20 to 35 percent compared to the synchronous path in PostgreSQL 17. The benefit is largest for analytical queries and maintenance operations that touch many pages. For latency-sensitive OLTP, the improvement is smaller but still meaningful because backends spend less time blocked on reads.
effective_io_concurrency and maintenance_io_concurrency
PostgreSQL 18 raises the default of
effective_io_concurrency to 16, reflecting the reality that most production databases now run on SSDs. For NVMe-backed systems, MinervaDB recommends pushing this to between 128 and 256 so the planner and executor issue enough concurrent prefetch requests to saturate the device queue depth. The companion parameter
maintenance_io_concurrency governs vacuum and index builds; a value of 64 to 128 keeps background maintenance from starving foreground traffic while still finishing quickly.
Memory Parameters and PostgreSQL 18 Performance
Memory tuning remains the foundation of PostgreSQL 18 performance. The classic parameters still apply, but their interaction with the new I/O layer changes the calculus slightly.
shared_buffers
The default
shared_buffers of 128MB is a compatibility choice, not a performance one. MinervaDB recommends starting at 25 percent of total system RAM for dedicated database servers. On a 64GB machine, that means roughly 16GB. Beyond 40 percent you often see diminishing returns because the operating system page cache also holds PostgreSQL data, and double buffering wastes memory. Validate your buffer cache hit ratio using
pg_stat_io, which is far richer in PostgreSQL 18.
work_mem
The
work_mem parameter controls how much memory each sort, hash, and grouping operation may use before spilling to disk. It is per-operation, not per-connection, so a single complex query can consume several multiples of the configured value. MinervaDB recommends 16 to 64MB for typical mixed workloads, but the ceiling must respect your peak concurrency. A safe formula is: available RAM for queries divided by (max_connections multiplied by expected operations per query). Over-provisioning
work_mem is the single most common cause of memory exhaustion we diagnose in production PostgreSQL clusters.
Vacuum and Autovacuum Changes in PostgreSQL 18
Vacuum is where many PostgreSQL 18 performance problems quietly originate. Bloat accumulates, transaction ID wraparound looms, and autovacuum either runs too aggressively or not enough. PostgreSQL 18 introduces several parameters that give operators finer control.
Eager Freezing with vacuum_max_eager_freeze_failure_rate
PostgreSQL 18 adds eager freezing, which proactively freezes pages during ordinary vacuum passes to reduce the cost of future anti-wraparound vacuums. The new
vacuum_max_eager_freeze_failure_rate parameter, defaulting to 0.03, caps how many freeze attempts may fail before eager freezing backs off on a given table. MinervaDB advises keeping the default for most workloads. Raise it only when you have append-mostly tables that would benefit from more aggressive freezing, and monitor the impact on write amplification.
autovacuum_worker_slots and Dynamic Workers
PostgreSQL 18 replaces the older static worker limit with
autovacuum_worker_slots, which reserves shared memory for autovacuum workers so you can raise the number of active workers without a restart. MinervaDB recommends 16 slots for most clusters and up to 32 for large, high-churn systems with hundreds of active tables. Pair this with a tuned
autovacuum_vacuum_cost_limit so the additional workers actually make progress rather than throttling each other.
autovacuum_vacuum_max_threshold
Large tables historically suffered because the scaled autovacuum trigger required an enormous absolute number of dead tuples before firing. The new
autovacuum_vacuum_max_threshold places an upper bound on that calculation, ensuring big tables get vacuumed on a sensible cadence. Tune this based on each table's update and delete rate; MinervaDB typically lowers it on hot tables to keep bloat and index health under control.
Query Planner and Parallelism Improvements
PostgreSQL 18 sharpens the query planner in ways that directly influence performance without any manual intervention, but a few knobs deserve attention.
Self-Join Elimination
The planner can now eliminate redundant self-joins when it can prove they produce identical rows, controlled by
enable_self_join_elimination. This is on by default and should stay on. It particularly helps ORM-generated queries and views that inadvertently join a table to itself. The result is fewer scans and lower CPU usage for a common query anti-pattern.
Parallel Query Tuning
For analytical workloads, MinervaDB recommends raising
max_parallel_workers_per_gather from the default of 2 to between 4 and 8, provided
max_parallel_workers and
max_worker_processes are sized to match. PostgreSQL 18 improves parallel-friendly plans, including better handling of parallel scans over the new asynchronous I/O path. As always, more parallelism helps large scans but can hurt short OLTP queries, so segment these settings by workload using per-role or per-database overrides.
Observability: Tuning What You Can Measure
You cannot tune PostgreSQL 18 performance without visibility. This release expands instrumentation considerably. The
pg_stat_io view now breaks down reads, writes, and extends by backend type and I/O context, letting you see exactly where the new asynchronous subsystem spends its time. The
track_cost_delay_timing parameter, when enabled, records how long vacuum and analyze operations spend sleeping due to cost-based delays—invaluable when diagnosing slow autovacuum.
MinervaDB recommends enabling
track_cost_delay_timing during active tuning windows and disabling it once your configuration stabilizes, since continuous timing adds a small overhead. Combine these views with
pg_stat_statements and per-query
EXPLAIN (ANALYZE, BUFFERS) output to build a complete performance picture before changing any parameter.
A Practical PostgreSQL 18 Tuning Workflow
Configuration matrices are useful, but disciplined process turns them into results. MinervaDB applies the following workflow when tuning PostgreSQL 18 performance for clients.
First, establish a baseline. Capture throughput, p95 and p99 latency, buffer cache hit ratio, and I/O wait under representative load before changing anything. Second, change one subsystem at a time—memory, then I/O, then vacuum—so you can attribute improvements or regressions correctly. Third, load test after each change using a workload that mirrors production, not a synthetic benchmark that exercises paths you never use. Fourth, monitor for at least one full business cycle, because vacuum and checkpoint behavior only reveal themselves over hours or days. Finally, document every parameter and its rationale so future operators understand why the configuration looks the way it does.
Common PostgreSQL 18 Configuration Mistakes to Avoid
Across dozens of engagements, MinervaDB sees the same PostgreSQL 18 performance mistakes repeatedly. Setting
shared_buffers to 80 percent of RAM in the mistaken belief that bigger is always better. Raising
work_mem globally to fix one slow report and triggering out-of-memory events under concurrency. Enabling
io_uring without confirming kernel support, causing PostgreSQL to silently fall back and mask the misconfiguration. Disabling autovacuum to reduce load, which trades short-term relief for catastrophic bloat and eventual wraparound emergencies. Each of these is avoidable with the matrix and workflow above.
Frequently Asked Questions About PostgreSQL 18 Performance
Should I enable io_uring immediately after upgrading to PostgreSQL 18?
Enable it only after confirming your kernel supports
io_uring and your security policy permits it. On supported systems it is one of the highest-impact PostgreSQL 18 performance settings, but on unsupported kernels PostgreSQL falls back to the worker method, so verify with
pg_stat_io that asynchronous I/O is actually active.
How much should I increase work_mem in PostgreSQL 18?
Start at 16 to 64MB and always divide your query memory budget by peak concurrency. Because
work_mem is allocated per operation, aggressive values are dangerous on high-connection systems. Use per-role settings for reporting users who genuinely need more.
Does PostgreSQL 18 change how autovacuum works?
Yes. PostgreSQL 18 adds eager freezing, dynamic autovacuum worker slots, and an upper threshold for large-table vacuuming. Together these reduce wraparound risk and keep big tables healthier without constant manual intervention.
Conclusion: Get PostgreSQL 18 Performance Right from Day One
PostgreSQL 18 delivers real, measurable performance gains—but only for operators who engage with its new configuration surface. The asynchronous I/O subsystem, refined vacuum controls, smarter planner, and richer observability collectively represent the most impactful tuning opportunity in recent PostgreSQL history. Use the configuration matrix in this guide as your starting point, follow a disciplined one-change-at-a-time workflow, and validate every setting against your own metrics.
MinervaDB specializes in PostgreSQL performance engineering, from parameter tuning to architecture reviews and 24/7 production support. If you want expert help extracting maximum PostgreSQL 18 performance from your infrastructure, our
expert PostgreSQL support team is ready to assist. Start with the parameters above, measure relentlessly, and let the data guide your next change.
Deep Dive: Sizing the Asynchronous I/O Subsystem for Your Workload
The introduction of asynchronous I/O in PostgreSQL 18 deserves more than a single paragraph, because sizing it correctly is where most of the performance upside lives. The core idea is that PostgreSQL can now issue multiple outstanding read requests to storage and continue working while the kernel services them. Under the older synchronous model, a backend performing a sequential scan would read one block, wait, read the next block, and wait again, leaving expensive NVMe hardware idle for most of the query. The new model keeps the device queue full.
To size this correctly, MinervaDB recommends thinking in terms of device queue depth. A single SATA SSD is comfortable with a queue depth of 32, while a modern enterprise NVMe drive can absorb 256 or more concurrent requests before latency degrades. Your
effective_io_concurrency value should approximate the useful queue depth of the underlying device divided by the number of concurrent backends you expect to be scanning simultaneously. Setting it too high wastes prefetch effort on blocks you never use; setting it too low leaves the device underutilized. Measure the sweet spot empirically by running representative analytical queries while watching device utilization with tools such as
iostat and correlating against PostgreSQL 18 performance metrics from
pg_stat_io.
When using
io_method = worker, the
io_workers pool becomes the constraint. These shared worker processes perform I/O on behalf of all backends, so they must be numerous enough that no backend waits for a free worker, yet not so numerous that they consume scheduler time and memory needlessly. For most production servers a value of 4 to 8 is appropriate, scaling toward the upper end on machines with many cores and fast storage. Because
io_uring pushes the concurrency into the kernel rather than PostgreSQL worker processes, systems that adopt it can often reduce their reliance on the worker pool entirely, simplifying the tuning surface.
Checkpoint, WAL, and Background Writer Interactions
No discussion of PostgreSQL 18 performance is complete without addressing the write path, because I/O tuning that ignores checkpoints and write-ahead logging will disappoint. PostgreSQL 18 continues to rely on
checkpoint_timeout,
max_wal_size, and
checkpoint_completion_target to spread checkpoint writes across time. MinervaDB recommends a
checkpoint_timeout of 15 minutes for most OLTP systems, a generous
max_wal_size sized to hold well over a checkpoint interval of writes, and a
checkpoint_completion_target of 0.9 so dirty pages flush smoothly rather than in a disruptive burst.
The new asynchronous I/O layer interacts favorably with the background writer, since dirty page flushes can now overlap with foreground work more effectively. However, if your
max_wal_size is too small, PostgreSQL will trigger checkpoints based on WAL volume rather than time, producing frequent, expensive write storms that no amount of read-side I/O tuning will fix. Watch the
pg_stat_bgwriter and checkpoint statistics to confirm that checkpoints are timed rather than requested. A high ratio of requested checkpoints is a clear signal to raise
max_wal_size.
Connection Management and Its Effect on Memory Parameters
Connection count is the hidden multiplier behind nearly every PostgreSQL 18 performance memory calculation. Because parameters such as
work_mem are allocated per operation and PostgreSQL uses a process-per-connection model, a cluster configured for 500 connections behaves very differently from one configured for 50. MinervaDB strongly recommends placing a connection pooler such as PgBouncer in front of PostgreSQL, running in transaction pooling mode where the application permits it. A pooler lets you keep
max_connections modest—often between 100 and 200—while still serving thousands of client sessions.
With a disciplined connection budget, you can afford more generous per-operation memory. A server that truly caps concurrent active queries at 100 can safely run a higher
work_mem than one that allows 1000 unpooled connections. This is why MinervaDB treats connection management and memory tuning as a single exercise rather than two independent settings. Tune them together, and validate the combined worst case: peak concurrent queries multiplied by the number of memory-intensive operations each query performs, multiplied by
work_mem, must stay comfortably below available RAM after accounting for
shared_buffers and the operating system.
Storage and Filesystem Considerations Under PostgreSQL 18
The benefits of the new I/O subsystem are only as good as the storage beneath it. MinervaDB recommends provisioning PostgreSQL 18 on local NVMe or high-performance network block storage with predictable latency. Filesystem choice matters too: XFS and ext4 remain the well-trodden, reliable options on Linux, and both support the
io_uring interface that PostgreSQL 18 can exploit. Ensure your kernel is recent enough to expose a mature
io_uring implementation, because early kernel versions had known limitations that reduce the very benefits you are trying to capture.
Mount options and readahead settings also influence results. With PostgreSQL now managing prefetch through asynchronous I/O, excessively aggressive kernel readahead can become counterproductive, fetching blocks that PostgreSQL would have requested more precisely on its own. As part of a tuning engagement, MinervaDB evaluates the entire stack from parameter to filesystem to device, because a bottleneck anywhere in that chain caps the PostgreSQL 18 performance you can achieve regardless of how carefully you set individual parameters.