DEV Community

HaoPeng Zhang
HaoPeng Zhang

Posted on

My n8n Kept Every Execution for 90 Days — Until the Database Hit 38 GB

My n8n Kept Every Execution for 90 Days — Until the Database Hit 38 GB

Part of my build-in-public series on running LLM workflows in n8n in production. This week's incident: my self-hosted n8n started taking 20 seconds to load the execution list, and the disk-usage graph had a slope I could see from across the room.

The morning the execution list stopped loading

I noticed it while debugging a failed run — I clicked "Executions" and the page just sat there. Twenty seconds of spinner before anything rendered. Then I checked the disk: the Postgres volume behind my n8n instance was at 38 GB, and it had grown by roughly 4 GB in the previous month alone.

The culprit wasn't user data. It was execution history. n8n's default is to keep everything — every run, every node input and output, full binary blobs for the ones that touched files — and I had never once changed the defaults. Sixty days of cron schedules across a dozen workflows, each one dutifully archiving its entire payload, including the fat ones.

Here's the part that stung: I knew about the pruning settings. I had seen them in the docs months earlier and filed them under "I'll configure that when it matters." It mattered at 38 GB, during a debugging session where I couldn't even open the page I needed.

What actually eats the space

A quick query told the story. The execution_entity table was most of it, and inside that, it was the data column — the full JSON of every node's input/output for every run. My workflows process email threads, SEO drafts, and content repurposing jobs. A single SEO draft run carries a 40 KB article body through six nodes, and each node stores its own copy. Multiply by the schedule (hourly for some), and you're storing the same 40 KB article dozens of times a day in slightly different JSON envelopes.

Binary data is worse. Any workflow that downloads an image or PDF keeps the blob inline unless you tell n8n to store it on disk instead. I had one workflow fetching product images for a content pipeline — every execution archived every image, and Postgres does not forgive that.

The fix, in three levels

Level 1: stop saving what you don't need (ten minutes). n8n's execution settings let you choose what gets persisted per workflow: save successful executions, save failed ones, or save nothing. For my high-frequency, low-risk workflows — the ones that succeed 99% of the time — I switched to saving only failed executions. The debugging value of a successful run's full payload is near zero; the failed ones are the ones I actually open. This one change cut new-data growth by roughly 80% overnight.

Level 2: prune on a schedule, not on hope. For the workflows where I still wanted history (the LLM ones, where I occasionally audit prompt behavior), I set explicit pruning: keep 14 days of successful executions, keep 90 days of failed ones. n8n's data-pruning settings (EXECUTIONS_DATA_PRUNE=true, with max age and interval) handle this automatically — no cron job of my own needed. Old successful runs evaporate quietly; failures stick around long enough for post-mortems.

Level 3: treat disk as a metric, not a surprise. I added a tiny meta-workflow — ironic, yes — that runs daily and checks the database size. If it crosses a threshold, it sends me a message. Storage is now something I get warned about, not something I discover during a debugging session at 11 PM. The whole thing is one Postgres node and an IF node. It took fifteen minutes to build and has already paid for itself by making the invisible visible.

The honest numbers

Before: 38 GB, growing ~4 GB/month, execution list taking 20+ seconds to load. After six weeks of the three-level policy: 6.2 GB, flat week over week, execution list loads in under two seconds. The 90-day failure retention I kept means I still have every incident I might want to investigate — I just stopped archiving thousands of successful runs nobody will ever open.

The uncomfortable truth: I didn't need 90 days of successful executions. I needed 90 days of failures and about two weeks of successes. The default setting isn't wrong — it's just optimized for "never lose anything," which is a great default for data and a terrible one for a database.

The one-line rule

Successful executions are logs, not assets. Keep the failures long, the successes short, and put disk usage on your daily dashboard before it puts itself there.

If you're self-hosting n8n, go check your execution settings right now. I'd bet at least one workflow is still on "save everything" — and your disk knows it.


I'm building AI Automation Lab — production-ready n8n workflow templates. The free AI Hook Generator is where most people start; the AI Inbox Triage & Reply Drafter is the paid workflow that taught me most of these production lessons. My open-source patterns live at github.com/zhp910318/n8n-ai-automation-lab.

Top comments (0)