Best practice for keeping 30+ days of execution logs in a high-volume self-hosted n8n instance

Hi everyone,

We’re running a self-hosted n8n instance (v2.32.7) on a Hostinger VPS in a production environment.

Our instance executes over 1,000 workflows per day (around 100k production executions according to the Insights dashboard), and we’d like to keep at least 30 days of execution history for auditing and troubleshooting purposes, including execution data (inputs, outputs and errors).

We are aware of the execution pruning settings:

EXECUTIONS_DATA_PRUNE=true
EXECUTIONS_DATA_MAX_AGE=720
EXECUTIONS_DATA_PRUNE_MAX_COUNT=10000
EXECUTIONS_DATA_PRUNE_INTERVAL=3600

Our goal was to retain approximately one month of execution data, so we increased the retention settings accordingly. However, after making these changes, the n8n instance started crashing repeatedly (4 crashes within less than an hour). We reverted the configuration to restore stability.

Some additional context:

  • Self-hosted on Hostinger VPS
  • 16 GB RAM
  • n8n v2.32.7
  • PostgreSQL database
  • Production environment with many active workflows
  • We need complete execution data for observability and audit (successful and failed executions).

My questions are:

  1. How do companies running high-volume n8n instances usually retain execution logs for 30+ days?
  2. Do you keep execution data directly in PostgreSQL, or export it to another observability/logging platform?
  3. Is there a recommended architecture for long-term execution history?
  4. Are there environment variables or database optimizations that should be considered before increasing execution retention?
  5. Has anyone experienced crashes after increasing execution retention? If so, what was the root cause?
  6. Is storing one month of full execution data in PostgreSQL considered an anti-pattern for n8n at this scale, or is it a common production setup?

Our objective is to have complete auditability without compromising instance stability.

Any recommendations or examples of production setups would be greatly appreciated.

Thanks!

Hi @mellkadvescalavel

Storing 30 days of full execution payloads in n8n’s operational PostgreSQL database at your execution volume causes severe table bloat and memory exhaustion (OOM) during UI/API queries, which is why your instance is crashing; the production-grade solution is to decouple short-term operational retention from long-term audit logging.

Hello @mellkadvescalavel ! That’s a big setup you have! Your prune count is set to 10,000 so even at the 30 day set your getting clipped around 10 days around 1000 runs a day! Before you raise or change any setting though, when it crashed was it the n8n container running out of memory or postgres running out of disk?

As per your questions, here’s my recommendations, and also having only 16GB ram for 1000 executions a day seems pretty tight.

  1. I’m not a company so I’m not sure, but I see a lot of people using pruning and loop nodes to handle limitations. Don’t keep[ it in n8n, Postgres can hold a window of 7-14 days for debugging but anything longer than that should go to a other logger or location
  2. You should export, push data you need to another source.

3, you should use two tiers, postgres with large pruning, and than outcomes to your own seperate store or location. Perhaps a web hook or something that can receive.
Something like N8N_EXECUTION_DATA_STORAGE_MODE=s3 to keep postgres tiny

  1. yes, these work - EXECUTIONS_DATA_SAVE_ON_SUCCESS=none - Keeps only errors

  2. For the crashes, its either postgres or OOM errors.

  3. At your scale, it is probably an anti pattern, its what will break ur DB and cause more issues.

Those two retention settings conflict. EXECUTIONS_DATA_PRUNE_MAX_COUNT=10000 caps retained executions at 10,000 even if EXECUTIONS_DATA_MAX_AGE=720. At about 1,000 runs a day, the count limit wins after roughly ten days.

I would not call the crashes table bloat or RAM failure yet. Check the container exit reason and PostgreSQL logs separately. Also verify free disk space. Each points to a different failure and a different fix.

For a 30 day audit trail, keep a shorter diagnostic window in n8n and write a narrow audit record from the workflow itself. Include the execution ID, workflow version, timestamps, outcome, and only the business identifiers needed to trace the action. Keep full payloads only when the audit requirement needs them, after removing secrets and personal data.

Measure one normal day of retained execution data, then project it across 30 days. Execution count alone does not tell you whether this Postgres instance and disk can carry the window.