n8n logon8n Automation Hub
Build breakdown

Using Google Sheets as a Real Queue in n8n (And the Ways It Bites You)

Build breakdown · n8n workflow · August 24, 2026

Google Sheets is not anyone's idea of a real database, and for a lot of what I build, that's exactly why it works — it's free, the client can open it and see the queue state in plain language, and I can fix a stuck row by hand in ten seconds without touching code. It's also caused two of the closest calls I've had in production, both from the same root cause: trusting that the Sheets node knows where your data actually starts and ends.

Workflow at a glance
  1. A Sheet holds the queue — one row per item, a status column tracks its state
  2. A scheduled or webhook trigger reads the next "ready" row and processes it
  3. On success, the row gets updated in place, not re-appended
  4. A separate append path handles new incoming items
  5. Both paths get a verify-read before and after any write that touches existing data
Screenshot of the actual n8n workflow canvas: the B2B Lead Gen Feed's scraper, dedup filter, and Sheets append/read nodes

The actual workflow canvas, straight from my n8n instance.

The webhook-array bug that writes one garbage row instead of many

If you POST a JSON array to an n8n webhook expecting it to fan out into multiple items downstream, it won't — the webhook node treats the whole payload as a single item, with your array nested inside its body field. Feed that straight into a Google Sheets append node with auto-mapped columns, and you get exactly one new row, and it's garbage: the webhook's own envelope fields, not your data. No error, no warning, just a wrong row sitting in your sheet. The fix is a one-line Code node between the webhook and the Sheets node that maps the array out into real items first.

The append that silently overwrote nineteen rows

The more dangerous bug: n8n's Google Sheets node auto-detects the table's boundaries when you don't give it an explicit range, and it gets this wrong if there's any row near the top whose shape doesn't match the rest — a leftover header artifact with real data sitting in columns that are otherwise blank for every real row is enough to confuse both the read and the append operations. A "read" that returns far fewer rows than you expect is the warning sign. I've had this actually happen: an append that should have landed at the end of the sheet instead overwrote existing rows in place, because the node's idea of "where the table ends" was wrong. Recovery meant reconstructing the destroyed rows from an earlier snapshot — which only existed because I happened to have pulled the data for an unrelated check minutes before. I don't build a queue on a fresh Sheet now without confirming the boundary detection is sane first.

matchingColumns is not as safe as it looks

`appendOrUpdate` matched on a column that looks unique but isn't is the other way to lose data quietly. If a queue reuses the same identifier across different items — the same slug across different weeks, the same step-name across different jobs — matching on it will silently overwrite an unrelated row that happens to share the value, including a row that's already marked complete. The fix is boring but absolute: only match on a column you've actually confirmed is unique across the entire sheet, and prefer appending without matching at all when you're inserting genuinely new rows rather than updating a known one.

The habit that would have prevented both

Every one of these was caught, not prevented, by a habit I now apply everywhere: a "success" response from the Sheets node is necessary, not sufficient. I do a verify-read before any write that touches existing rows, and another one immediately after, to confirm the actual row count and content match what I expect — not just what the node claims happened.

Running something on a spreadsheet that's outgrown "just a list"?

I build reliable Sheets-backed queues and databases for n8n workflows, with the boundary and dedup safeguards most tutorials skip.