PostgreSQL work_mem Calculator

July 22, 2026

PostgreSQL query memory planning

PostgreSQL work_mem Calculator

Estimate worst-case PostgreSQL query memory, safe work_mem, spill risk, temp file pressure, and RAM headroom from active query concurrency, sort/hash nodes, shared buffers, maintenance memory, OS reserve, and observed temp file rate.

⚙Postgres query presets
📊Memory and spill inputs
Queries actively sorting, hashing, aggregating, or joining at the same time.
Count Sort, Hash, HashAggregate, Materialize, Memoize, and similar memory nodes.
PostgreSQL work_mem value used per eligible plan node, not per server.
maintenance_work_mem or autovacuum_work_mem budget per maintenance worker.
Autovacuum, CREATE INDEX, REINDEX, VACUUM, or ALTER TABLE jobs that may overlap.
Use the VM or container memory limit when PostgreSQL is not on bare metal.
Fixed shared memory cache allocated by PostgreSQL at startup.
RAM left for kernel, page cache, filesystem, agents, backups, and other services.
Background workers, connection overhead, extensions, WAL buffers, and safety margin.
Controls how aggressively the calculator lowers safe work_mem for concurrency.
Observed from log_temp_files, pg_stat_database.temp_bytes, or monitoring.
Use 1 for nonparallel plans, 2 to 5 when parallel workers may allocate memory too.
Worst-case query memory
-
active queries x nodes x work_mem
Upper planning envelope for query memory.
Safe work_mem
-
per memory operation
Based on available RAM and spill tolerance.
Spill risk
-
temp file pressure and sizing
Uses temp rate plus current work_mem ratio.
Memory headroom
-
after modeled worst case
RAM left after fixed and variable memory.
Enter values to calculate PostgreSQL work_mem.

Worst-case memory breakdown

Total modeled RAM use-

Recommended setting grid

Temp spill pressure-
🗃PostgreSQL memory setting grid
SettingScopeTypical planning rangeOversizing risk
work_memPer sort, hash, materialize, memoize, and similar node4 MB to 64 MB for OLTP; 64 MB to 512 MB for scoped analyticsMultiplies by active queries, plan nodes, and sometimes parallel workers.
hash_mem_multiplierMultiplier allowing hash operations to exceed work_mem1.0 to 2.0 in many modern PostgreSQL installsHash joins and aggregates can consume more memory than a simple work_mem count.
maintenance_work_memPer maintenance task such as VACUUM, CREATE INDEX, and ALTER TABLE256 MB to 2 GB, higher for controlled maintenance windowsSeveral autovacuum workers or index jobs can allocate it at the same time.
shared_buffersPostgreSQL shared page cache allocated at startup20% to 35% of RAM on many dedicated database hostsLeaves less space for query memory, OS cache, and maintenance work.
temp_file_limitPer session limit for temporary filesUse role-specific limits for reporting and risky ad hoc usersToo low breaks legitimate reports; too high allows disks to fill.
log_temp_filesLogging threshold for temp files created by queries0 during investigation, then a practical threshold such as 64 MBNo memory risk, but verbose logging can be noisy during heavy spills.
📋Query preset reference table
PresetActive queriesNodes per queryStarting work_memUse case
Home Lab OLTP81.58 MBSmall services, simple joins, and light concurrent traffic.
Small SaaS API24216 MBModerate API workload with index lookups and occasional sorts.
Read Heavy API481.816 MBMany active reads where memory should remain conservative.
Reporting Dashboard16464 MBDashboards with grouped reports, ORDER BY, and hash aggregates.
Hash Join ETL105128 MBETL batches where hash joins and aggregates dominate plans.
Parallel Analytics84128 MBParallel reports where workers multiply query memory.
Timeseries Rollup18348 MBContinuous aggregation over hot partitions or recent events.
GIS Sort Search123.596 MBSpatial search and distance sorts with larger intermediate sets.
Bulk Index Build4232 MBFew client queries, but high maintenance memory pressure.
PgBouncer OLTP601.48 MBHigh client fan-in where transaction pooling limits backends.
Container Postgres1428 MBMemory-limited Docker or Kubernetes PostgreSQL instances.
Temp Spill Incident204.532 MBCurrent workload shows high temp file growth and slow sorts.
🛠Spill risk and temp file reference
SignalLow riskWatchHigh riskAction
Temp file rateBelow 0.5 GB/hour0.5 to 5 GB/hourAbove 5 GB/hourInspect plans, add indexes, or use targeted work_mem increases.
Current vs safe work_memCurrent at or below safe valueUp to 125% of safe valueMore than 125% of safe valueLower global work_mem or scope higher values to reporting roles.
Headroom after worst caseAbove 15% RAM8% to 15% RAMBelow 8% RAM or negativeReduce concurrency, memory nodes, maintenance overlap, or shared buffers.
Parallel worker multiplier1 to 1.51.5 to 3Above 3Review max_parallel_workers_per_gather and plan shapes.
Spill toleranceBatch/reporting allowedOccasional spills allowedLatency-sensitive or no spillsUse lower global values plus SET LOCAL work_mem for known jobs.
🔍How to interpret work_mem results

Worst-case memory

The estimate multiplies active queries, memory nodes, current work_mem, and the parallel multiplier. It is a planning envelope, not a guaranteed allocation.

Safe work_mem

The safe value is the available query RAM divided by active memory operations, then adjusted by spill tolerance so global settings do not assume perfect behavior.

Spill risk

Risk rises when current work_mem is above safe work_mem, headroom is tight, temp files grow quickly, or many memory nodes appear in active plans.

Headroom

Headroom is RAM left after shared_buffers, OS reserve, maintenance memory, base overhead, and worst-case query memory are modeled together.

⚖Global, role, and session strategies

Low global work_mem

Best for high-concurrency OLTP. Keep the global value conservative, then raise memory only for known jobs, roles, or sessions that need it.

Role-specific work_mem

Use ALTER ROLE or ALTER DATABASE for reporting users, ETL users, or admin sessions so ordinary app traffic does not inherit large memory grants.

SET LOCAL for jobs

Batch jobs can set work_mem inside a transaction, run the heavy query, and return to the normal value after commit or rollback.

Query-plan fixes

Indexes, better statistics, lower row estimates, rewritten joins, and partition pruning can reduce the number and size of memory-hungry nodes.

Temp file limits

temp_file_limit can stop runaway spills from filling storage. Pair it with monitoring so failed reports are investigated instead of ignored.

Parallelism control

Parallel workers can make one query allocate memory several times. Tune max_parallel_workers_per_gather for workloads with large sorts or hashes.

💡PostgreSQL work_mem tips
Count operations, not queries.A single query can use several memory nodes at once. EXPLAIN plans with Sort, Hash, HashAggregate, Materialize, Memoize, and Gather nodes deserve special attention.
Use active concurrency.max_connections is usually the wrong multiplier. Use active queries from monitoring, pg_stat_activity, app pool metrics, and realistic peak traffic windows.
Measure spills directly.Enable log_temp_files during investigation and compare pg_stat_database.temp_bytes before and after busy windows to find actual temp file pressure.
Avoid one giant global value.Large global work_mem values feel helpful until a burst of concurrent hash joins allocates memory across many backends and workers.
Separate OLTP from reporting.Latency-sensitive app roles often need a small global work_mem, while reporting roles can receive larger scoped settings and tighter temp_file_limit controls.
Review maintenance overlap.Autovacuum, CREATE INDEX, REINDEX, and bulk loads consume maintenance memory separately from work_mem, so include them in the same RAM budget.

If you’ve ever heard of work_mem, you may have assumed that it’s simply a one-off value you enter into postgresql.conf and move on. Nope! It’s a multiplier, and arguably the most dangerous PostgreSQL configuration parameter, since it multiplies by the amount of concurrent activity on your system. Each query gets its own portion of RAM for aggregations, sorts, and hash joins.

Trouble occurs when lots of user is running resource-intensive queries concurrently and the database attempts to consume more RAM than actualy available. Out-of-memory errors result, which in turn stall your applications.

How to Set Work_Mem in PostgreSQL

To do this, just enter your node count and concurrency into calculator above, and it will do the work for you. There’s no need to multiply together multiple variables in your head, which never line up anyway in real world. Most folks freak out when they look at max_connections and realize they have hundreds of connection, so they must reserve lots of memory.

But that ignores the fact that most of those connections aren’t doing anything; and you only want to care about the ones that are actually running at any given time. So instead of asking how much capacity there could be, it asks how many active query are running at same time. It makes you consider reality (what happens on your busiest hour) versus theory (theoretical maximums).

Why does that matter? To understand that, we need to look under the hood at a query plan. For a basic select statement, there may be nothing special about it except what comes out of indexes. Adding something like a group by or order by clause will add some sort or hash nodes to the plan and these nodes claim memory (i.e. Work_mem). If your query has three such node, it could easily triple amount of memory used before completing.

The calculator allows you to account for this by defining how many memory nodes per query you tend to see. You need to look at real execution plans to know whether your reports use two memory nodes or five, which means that experience trumps guessing here as well.

And then you have the operating system reserve. Don’t forget Linux also require memory! Keeping the commonly used pages out of the shared_buffers (via the kernel cache) really helps PostgreSQL. Filling every byte with db settings will starve the OS and hurt performance. By including an OS reserve and some memory for maintenance such as auto vacuum workers, we make sure you’re calculating based off something real world. It leaves headroom so background tasks like this can run and keep the system healthy.

When a node’s available memory runs out, it will write excess to disk in what’s called “spill” files. This kills query speed because disk is slower than RAM. Based on your settings for tolerance and temp file rate, this calculator estimates spill risk. Small spills can hurt performance of latency sensitive API requests; occasional spills can be fine for batch reports as long as it doesn’t crash the server. Knowing which camp you fall into will change how aggressively you tune.

Instead of setting work_mem globally, you should set it as high as needed for certain sessions/roles and lower otherwise. That way, tasks that can do some heavy lifting will have room to breathe, but you avoid having a runaway query using up all the memory. The page also has a useful table showing typical workload types and their recommended settings (ranging from loose to strict). For instance, an ETL pipeline could afford to be more generous with higher values for fewer large jobs than a read-heavy API serving a lot of smaller requests.

Because memory pressure varies with your query workload, and data volume increases over time, tuning is a continuous process. Monitor your temp file log; a spike in disk usage indicates something has gone wrong. Re-check the plans. Were some indexes missed? Are stats out of date? Increasing work_mem may help, but usually this simply hides the real problem. You want to use only what you need while maintaining consistent performance.

This isn’t so much about configuration as budgeting: You only have so much money. Should you place it into a single high-risk investment vehicle? Or should you invest some in each, making sure the lights stay on? Likewise, work_mem is how much you allocate per query. Allocate too little and they’ll crawl, disk spills! Allocate too much and… well, then you don’t really have any left.

That balance is what keeps your server responsive and your users happy. When the database runs out of room, it stops working, and that’s not fun.

PostgreSQL work_mem Calculator

Related posts

Leave a Comment