Skip to main content
RunBook Academy

PostgreSQLXI · Memory and Resource ManagementMemory

maintenance_work_mem and the operations that use it

Intermediate⏱ ~25 minpsql

What you'll learn

  • List the operations that use maintenance_work_mem and how each is bounded
  • Decide from evidence whether raising it will help
  • Separate autovacuum_work_mem from maintenance_work_mem and know why
  • Compute the aggregate commitment a large value represents

Prerequisites

Verified against PostgreSQL 18.x · PostgreSQL (comparison targets) 17.11, 16.15 · PostgreSQL (support calendar) 18, 17, 16, 15, 14 supported · pgBackRest 2.59.1 · PgBouncer 1.25.2 · Patroni 4.1.5 · Ubuntu (host baseline) 26.04 LTS · 2026-08-27

Not yet marked complete on this device.

maintenance_work_mem is the allowance for operations that are deliberate rather than continuous, and it is set generously by convention on the reasoning that only a few run at once.

That reasoning is sound. The assumption usually attached to it — that a generous value makes those operations faster — is worth testing.

What uses it

OperationHow the memory is used
CREATE INDEX, REINDEXThe sort workspace for building the index
VACUUMThe collection of dead tuple identifiers
ALTER TABLE forms that rewriteSorting and building the new relation
ADD FOREIGN KEY, VALIDATE CONSTRAINTVerification workspace
CLUSTERSorting the table into index order

autovacuum_work_mem overrides it for autovacuum workers, and defaults to -1, meaning “use maintenance_work_mem”.

Testing the index build claim

Read-only / Safethe same index built three times at different settings
$ psql -U postgres -c "SET maintenance_work_mem='NNN'; SET max_parallel_maintenance_workers=0; CREATE INDEX ordersx_amount_idx ON ordersx (amount)"
  maintenance_work_mem=4MB  :  874 ms
maintenance_work_mem=64MB : 1004 ms
maintenance_work_mem=1GB  :  946 ms

No useful difference, and the smallest setting produced the fastest run. The spread is within run-to-run variation.

Where it demonstrably matters

Vacuum, and the figure is in the output you are already reading.

INFO:  finished vacuuming "postgres.public.orders": index scans: 4

From lesson VIII-01: vacuum collects dead tuple identifiers in memory, then scans every index once to remove their entries. If the collection fills before the heap scan finishes, vacuum must stop, scan every index, empty the collection, and resume.

index scans: 4 means every index on that table was read four times. On a table with six indexes totalling 200 GB, that is 800 GB of reading instead of 200 GB.

The aggregate commitment

maintenance_work_mem is allocated per running maintenance operation, not once.

maintenance_work_mem × (autovacuum_max_workers + concurrent manual operations)

At 4 GB with three autovacuum workers, that is a 12 GB commitment before any manual REINDEX is considered. Add a deployment running two concurrent index builds and it is 20 GB.

Parallel index builds

max_parallel_maintenance_workers (default 2) lets CREATE INDEX use workers.

Each worker gets its own share of maintenance_work_mem — the total is divided, not multiplied, so the memory commitment does not grow with worker count. CREATE INDEX CONCURRENTLY makes two passes over the table and waits for older transactions between them, so it is substantially slower than a plain build.

The measurement above disabled parallel workers deliberately, to compare memory settings rather than worker counts.

What to take from this

  • Measured: no useful difference in a 2M-row index build across 4 MB, 64 MB and 1 GB. The benefit is not automatic.
  • index scans: N in VACUUM VERBOSE is the direct diagnostic. Above 1 means memory is the constraint.
  • The allowance is per operation. At 4 GB with three workers, that is a 12 GB commitment.
  • Set autovacuum_work_mem separately whenever maintenance_work_mem is large.
  • Parallel index build workers divide the allowance; CONCURRENTLY does not use them at all.
  • Vacuum’s memory structure changed in 17, so pre-17 advice about a 1 GB ceiling no longer applies.

Cross-course references

  • Linux for Production Sysadmins — Part XXXVII (Resource management) covers bounding a maintenance operation’s resource use at the host level, and Part XL (Memory Performance) covers what the peak actually costs.
  • Ansible for Production Sysadmins — Part XLVIII (Maintenance windows and rollback) covers setting this for the duration of an operation and putting it back.

Quiz

Knowledge check · 6 questions

  1. Q1. VACUUM VERBOSE on a large table reports 'index scans: 1'. What does raising maintenance_work_mem achieve for this table?

  2. Q2. A cluster sets maintenance_work_mem to 4 GB so that supervised REINDEX operations run well. What exposure has this created?

  3. Q3. Guidance from several years ago states there is no point setting maintenance_work_mem above 1 GB for vacuum. Is that still true?

  4. Q4. Which operations draw on maintenance_work_mem? Select all that apply.

  5. Q5. Raising maintenance_work_mem reliably speeds up index builds, since a larger sort workspace means fewer merge passes.

  6. Q6. How would you decide whether raising maintenance_work_mem would help a specific large table?

Passing score: 75%. Answers are checked in this browser.