PostgreSQL Vacuum & Autovacuum How Do You Tune It for High-Write Workloads

Hi everyone,
I’m running PostgreSQL behind a busy n8n deployment where workflows are constantly inserting, updating, and deleting data.
As the workload has grown, I’ve started wondering how much attention I should be giving to VACUUM and Autovacuum tuning. I know PostgreSQL handles this automatically, but I’m curious how people optimize it for production systems with heavy write activity.
UPDATE workflow_jobs
SET status = ‘completed’,
updated_at = NOW()
WHERE job_id = 1001;
Over time, I imagine this creates dead tuples that Autovacuum has to clean up.
For those running PostgreSQL in production:
Have you adjusted the default Autovacuum settings, or do the defaults work well?
Which metrics do you monitor to know when Autovacuum is falling behind?
Have you ever experienced table bloat or performance issues because of poor vacuuming?
Any tuning recommendations for databases with frequent updates and deletes?

Describe the problem/error/question

What is the error message (if any)?

Please share your workflow

(Select the nodes on your canvas and use the keyboard shortcuts CMD+C/CTRL+C and CMD+V/CTRL+V to copy and paste the workflow.)

Share the output returned by the last node

Information on your n8n setup

  • n8n version:
  • Database (default: SQLite):
  • n8n EXECUTIONS_PROCESS setting (default: own, main):
  • Running n8n via (Docker, npm, n8n cloud, desktop app):
  • Operating system:

Hey @Selena_Gloria, while you wait for a response, here are some things that might help:

Suggested resources

Automatically matched to your question.

Docs:

Forum:

@Danilov_Vovka, @Emmas, @Niffzy - you’ve helped with similar issues before, can you take a look?

Automatically suggested by n8n’s community bot. It’s a pilot - please share feedback here.

Hi @Selena_Gloria A good starting point is to let Autovacuum do its job, but monitor it as your workload grows.

Recommended approach

For most production systems, the default settings work well initially. As write activity increases, monitor how quickly Autovacuum keeps up with updates and deletes before changing any settings.

What to monitor

Keep an eye on:
Autovacuum frequency
Dead tuples
Table bloat
Long-running transactions
Query performance

These metrics can help you identify when vacuuming is falling behind.

Best practices
Run ANALYZE regularly so PostgreSQL has up-to-date statistics.
Avoid long-running transactions, as they can delay vacuuming.
Investigate tables with a high number of dead tuples.
Tune Autovacuum only after identifying a real performance bottleneck.

Make sure to avoid Disabling Autovacuum
Changing Autovacuum settings without monitoring the database first
Ignoring table bloat as your data grows

In many production environments, consistent monitoring and regular maintenance are more effective than aggressive Autovacuum tuning.

Hi @Selena_Gloria
On an n8n Postgres the churn is concentrated in two tables. n8n’s own execution pruning batch-deletes from execution_data and execution_entity, and Postgres only triggers autovacuum at 50 dead rows plus 20% of the table, so once those tables reach a few million rows they carry millions of dead tuples before autovacuum reaches them. Leave the global settings at their defaults and override just those two:

ALTER TABLE execution_data SET (
  autovacuum_vacuum_scale_factor = 0.02,
  autovacuum_vacuum_threshold = 1000,
  autovacuum_vacuum_cost_limit = 1000
);
ALTER TABLE execution_entity SET (
  autovacuum_vacuum_scale_factor = 0.02,
  autovacuum_vacuum_threshold = 1000,
  autovacuum_vacuum_cost_limit = 1000
);

On the n8n side, lowering EXECUTIONS_DATA_MAX_AGE and EXECUTIONS_DATA_PRUNE_MAX_COUNT keeps the delete backlog smaller, and EXECUTIONS_DATA_SAVE_ON_SUCCESS=none cuts the write volume at the source.

@Niffzy Have you ever had to tune Autovacuum for just one table instead of the entire database? If so, what signs told you that table needed different settings?

@Selena_Gloria Yes, I’ve tuned Autovacuum for individual tables instead of changing the settings for the entire database. This is useful when one table receives significantly more updates or deletes than others.

The main signs I look for are:
A high number of dead tuples
Frequent updates or deletes
Slower query performance
Table bloat increasing over time

Rather than applying aggressive settings globally, I prefer adjusting Autovacuum only for the tables that need it. This keeps maintenance efficient while avoiding unnecessary overhead on the rest of the database.