Skip to content
Skip to main content
An open burgundy leather index card file box holding cream ruled cards on a pale studio surface, a physical metaphor for n8n Data Tables as small native storage next to a full database
8 min readBy Carlos Aragon

n8n Data Tables vs Postgres: When to Use Each

Use a Data Table when the rows exist only to serve your workflows. Use Postgresthe moment anything outside n8n has to read or write them. That's the whole rule, and it's a question about ownership, not about how much data you have. Size only decides things later, when the 200 MiB instance-wide budget starts arguing back.

The Short Answer, By Situation

Stopping a webhook from processing the same event twice?Data Table. This is the single best thing they do, and I'll spend a section on it below.

Storing a marker so a nightly workflow knows where it left off?Data Table. One row, two columns, done. You were about to abuse a Google Sheet for this. Don't.

A lookup or mapping table you edit by hand a few times a year? Data Table. The built-in editor is genuinely nice, and the alternative was a hardcoded object in a Code node that nobody but you can safely change.

Leads, orders, contacts, anything a client will eventually want a dashboard on? Postgres. Every time. The instant a human wants to see the data outside n8n, workflow-private storage becomes a liability, and migrating later is worse than starting right.

An event log, message history, or anything that grows forever? Postgres. The 200 MiB budget is shared across your entire instance, and an append-only table will eat it and then break every other workflow that writes to storage.

What A Data Table Actually Is

It's a real table living inside n8n. You define typed columns, it holds rows, and a Data Table node reads and writes them from any workflow in the same project. No connection string, no second container, no credential to rotate. n8n calls it integrated data storage, which is accurate and undersells how much friction it removes.

Column types are string, number, boolean and date. Types are enforced on write, so pushing a string into a number column fails immediately at the node instead of poisoning three steps downstream — a genuine upgrade over the Google Sheets pattern it replaces, where everything is a string and you find out in the CRM.

The row operations are Insert, Get, Update, Delete, Upsert, If Row Exists and If Row Does Not Exist. Those last two are the interesting ones — they're branching operations, not data operations. They pass the input through on the matching side and let you route on the answer without writing a comparison yourself. Several operations also support a dry run, which is a considerate thing to ship and I've used it more than I expected.

The Four Limits That Send You To Postgres

None of these show up in the tutorials, and all four have cost somebody a rebuild.

1. The 200 MiB cap is instance-wide. Not per table, per instance. n8n warns at about 80% and again at 100%, and past the line you can't add rows by hand and executions that insert or update start failing. Do the arithmetic before you commit: at roughly 500 bytes a row that's on the order of 400,000 rows before overhead. Fine for markers and lookups, useless for logs. Self-hosted can raise it with N8N_DATA_TABLES_MAX_SIZE_BYTES. Cloud can't, so on Cloud it's a design constraint, not a setting — one more line in the cloud versus self-hosted ledger.

2. The Code node can't touch them.Direct programmatic access isn't supported, so every read and write is a node on the canvas. For a handful of lookups that's fine and arguably clearer. For a loop that needs to check two hundred ids, you're building a Split In Batches contraption to do what one SQL statement does. That's the signal you've outgrown it.

3. Tables are scoped to a project.A workflow in another project can't reach the table, which is correct behaviour and still surprises people who assumed instance-wide storage. If your automations are split across client projects and they need shared reference data, that's a database.

4. They don't travel with the workflow. This is the one that bites in production. Export a workflow, import it into your prod instance, and you get the nodes but not the table behind them. Nothing in the JSON carries it. If you're moving workflows through git — which you should be, and you can do it without the Enterprise plan — the table is a manual step on the other side, or a bootstrap workflow that creates and seeds it on first run. Write that bootstrap. Future you will not remember the schema.

There's a fifth, smaller one worth naming: no JSON or array column typeat the time of writing. Workflow items are nested objects, so you end up stringifying a payload into a string column and parsing it after the read — and losing the ability to filter on anything inside it. It's the most-requested addition in the community for a reason.

Side By Side

QuestionData TablePostgres
Setup costZero. It's already there.A container, a credential, a backup job.
Who can read itWorkflows in the same project only.Anything with the connection string.
Size ceiling200 MiB instance-wide by default.Whatever the disk holds.
Access from Code nodeNot supported.Yes, or the Postgres node.
Joins and aggregationNo. Filter conditions only.Full SQL.
Travels with a workflow exportNo — recreate it in prod.N/A, it's external by design.
Column typesString, number, boolean, date.Everything, including JSONB.
Best atDedupe markers, run state, small lookups.Business records anyone else will query.

The Use Case Worth Building Today

If you build one data table this week, build the idempotency ledger. Two columns: the external event id as a string, and a date. Drop an If Row Exists node right after the webhook trigger, route the match branch to a No Operation, and insert the id on the success path at the end.

That's it. That small table kills the entire class of bug where a provider retries a webhook, or a queue redelivers, or somebody double-clicks, and your workflow cheerfully charges a card twice. Before data tables you solved this with an external Redis or a Postgres table — real infrastructure, for a two-column problem. I've written up the full duplicate-execution pattern separately; the logic doesn't change, the storage just got a lot cheaper to stand up.

One caveat that matters: the ledger grows forever unless you prune it.Add a scheduled workflow that deletes rows older than your provider's retry window — thirty days is generous for most — or you will meet the 200 MiB cap from the least interesting direction possible.

If you do one thing today:

Open any workflow that starts with a webhook and ask what happens if the exact same payload arrives twice. If the honest answer is “it runs twice,” you have a ten-minute fix and a data table to put it in.

Where I Still Reach For A Real Database

Anything a client will look at. The moment there's a dashboard, an admin screen, or a “can you send me a CSV of last quarter” request, you want SQL and you want a second application able to connect. That's the whole argument behind the Supabase lead database build — n8n writes it, but n8n was never the only reader.

Anything an AI agent needs to search semantically, too. Data tables filter on conditions; they don't do similarity. That's a vector store question, and the answer is usually pgvector on the Postgres you already run.

And conversation memory. It looks like a data table job right up until you count the rows: every turn of every session, forever, in a 200 MiB budget shared with everything else. I compared Postgres and Redis for agent memory and neither answer was “keep it inside n8n.”

Here's the honest framing I use with clients: a data table is a private variable for your automation. A database is a shared recordof the business. Both are correct in their lane, and almost every mess I've been called in to fix came from someone storing a shared record in a private variable because it was faster on the day.

Frequently Asked Questions

Should I use n8n Data Tables or an external database?

Use a data table when the rows exist only to serve your workflows — dedupe markers, run state, small lookup and mapping tables, a queue of items to process. Use an external database such as Postgres when anything outside n8n reads or writes the same rows, when you need SQL joins and aggregation for reporting, when the dataset will grow past the storage budget, or when the data has retention and compliance requirements. The deciding question is ownership, not size: data tables are workflow-private storage, a database is shared storage.

What is the storage limit for n8n Data Tables?

By default the total storage used by all data tables in an instance is limited to 200 MiB, and that budget is instance-wide rather than per table. n8n warns you at roughly 80 percent of the limit and again when you reach it, and once you exceed it manual additions are disabled and workflow executions that try to insert or update rows fail. On self-hosted instances you can raise the ceiling with the N8N_DATA_TABLES_MAX_SIZE_BYTES environment variable. On n8n Cloud you cannot, so treat 200 MiB as a hard design constraint there.

Can I read an n8n Data Table from a Code node?

No. Direct programmatic access to data tables from the Code node is not supported, so every read and write goes through a Data Table node on the canvas. In practice this pushes lookups out of JavaScript and onto the graph, which is more readable but much clumsier when you need many lookups in one pass. If your design calls for querying storage inside code, use the Postgres node or a database client instead.

Do n8n Data Tables get deployed with my workflows?

The table definition and its rows are not part of a workflow export, so pushing a workflow through git or importing JSON into a production instance moves the nodes but not the storage behind them. You have to create the table in the target project yourself, or add a bootstrap workflow that creates and seeds it on first run. Tables are also scoped to a project, so a workflow in a different project cannot reach a table it does not own.

What column types do n8n Data Tables support?

String, number, boolean and date, with the type enforced at write time — try to write text into a number column and the execution fails loudly at the source instead of quietly corrupting downstream steps. There is no JSON or array column type at the time of writing, which is the limit most people hit first, because a workflow item is usually a nested object. The workaround is to stringify the payload into a string column and parse it after reading, and to accept that you cannot filter on anything inside that blob.

Not sure which half of your n8n stack belongs in a database?

I build n8n systems that survive a second client, a second project and a production import — idempotent webhooks, storage that lives in the right place, and a deployment path that doesn't depend on remembering a schema. If your workflows work on the canvas and get strange in production, that gap is usually two decisions deep and I can find it.

Related Posts

n8n

How to Stop Duplicate Executions in n8n (2026)

Duplicate executions are almost never an n8n bug — they are at-least-once webhook delivery, your own Retry On Fail setting, and a dedupe check that reads and writes in two separate steps. The Remove Duplicates node is a filter, not a lock, and it loses the race the moment you run queue mode with more than one worker. The atomic idempotency gate I run instead, the two-phase claim that stops duplicates turning into silent data loss, and which store to put it in.

n8n

n8n MCP Server vs MCP Server Trigger: Which One You Actually Need

n8n ships two features named some version of "MCP server" and they do opposite jobs. The built-in server is instance-level — one connection lets an AI client list, build, update and run workflows across your whole n8n, and since April 29 2026 it can author workflows from scratch. The MCP Server Trigger node is workflow-level, exposing only the tools you attach. Pick by blast radius, plus the queue-mode routing gotcha that silently breaks SSE.

n8n

n8n AI Agent Tool vs Sub-Workflow: When to Use Each

n8n gives you two ways for one agent to delegate to another. The AI Agent Tool node keeps the whole hierarchy on one canvas inside a single execution. The Call n8n Sub-Workflow tool hands the job to a separate workflow with its own execution record — one you can retry, replay, reuse from other workflows and test on its own. That execution boundary is the entire decision, not how complex the build is. Plus the sub-node trap that silently makes both process only the first item.