← All posts

How one n8n publish broke production for 11 minutes

A real post-mortem on n8n CE's publish-is-deploy: one broken expression, 11 minutes of broken AI responses for every user, and the CLI we built to prevent it.

At 4:47pm on a Tuesday, a developer on my team published a workflow in n8n. Eleven minutes later, users of a client’s app were getting errors on every single AI request. No alert, no warning, no audit trail pointing to what changed. Just: publish is deploy.

With n8n Community Edition, there’s no staging layer at the workflow level. You can save your changes all day without touching production. The moment you hit publish on an active workflow, though, it goes live. Immediately. Whatever broken state the workflow is in, that’s what your users get.

We’d been building on this the hard way for a while. This is the incident that made us stop.

TL;DR: n8n CE’s “publish is deploy” behavior means any in-progress workflow goes straight to production with no review gate. The fix: use Chiral, the n8n source-control CLI to diff, preview, and push between environments. chiral diff uses SHA-256 content fingerprinting to ignore n8n’s versionId noise, so you only see real changes.

4:47pm

Developer publishes workflow

Thought they were done. Hit publish.

4:47pm

Production gets broken expression

4:58pm

First user errors reported

Impact detected: 11 minutes since publish

4:59pm

Rollback attempted

Click 'import', pray we exported the right JSON yesterday

5:01pm

Service restored

14 minutes total. All from one publish.

11 min

of broken responses for every user

From a single publish with no safety gate

What we were building

We run n8n CE as the AI layer inside a few client Node.js applications. Same pattern across all of them: the app fires a webhook when a user triggers an AI feature, n8n builds the prompt, routes it to the right model (we use OpenRouter for most of it, with some workflows calling Claude, Gemini, or DeepSeek directly depending on the task), and POSTs the structured response back to the app. The features users actually see run on top of this pipeline.

The workflow that broke handles content processing for one client. Incoming payload, prompt construction, model routing, response formatting. We’d been reworking the prompt-building node to support a new document type. The work was, at that moment, half-done.

You can probably see where this is going.

What happened at 4:47pm

The developer was midway through a refactor of the “Build Prompt” node, specifically the part that constructs the messages array before the model call. He thought he was done with one particular change. Hit publish to see the output. Standard debugging reflex, the kind everyone does without thinking.

The workflow had a broken expression in that messages array. A reference to a field that didn’t exist in the new document type we were adding. The expression evaluated to undefined, which n8n dutifully passed through to the model API as a literal string. No 500. No exception thrown. The workflow executed just fine, from n8n’s perspective. It returned a 200 on every request. It just happened to be constructing garbage prompts and sending them to the model for 11 minutes straight before anyone noticed the outputs were wrong.

That’s the detail that stuck with me. Not the 11 minutes (we caught it relatively fast). It’s that there was no signal at all. No error, no alert, no log entry that said “hey, this expression looks wrong.” The workflow ran happily and returned garbage and n8n had absolutely no opinion about any of it.

Why n8n CE works this way

This isn’t a complaint about n8n. They built Source Control specifically because this failure mode is real, and the CE workarounds are all pretty bad. Source Control is a Business plan feature. €800/month per workspace, verified against n8n pricing in May 2026. For a small agency running CE across several client projects, that’s a hard sell when the thing you’re trying to solve is “safely moving a JSON file between two servers.”

So on CE you get: no staging or production separation at the workflow level. No diff before you push. No rollback. A publish is a deploy, full stop, with no gate between them. That’s the product. It’s a reasonable tradeoff at the free tier.

It still bit us.

The workarounds we tried

Manual export/import was the obvious first move. Export the stable version as JSON before touching anything, re-import it if something breaks. This lasted about three weeks before we gave up on it.

The ID regeneration makes the diffs unreadable. We had a workflow where we fixed a single typo in a node label and the resulting JSON diff was 200 lines, mostly versionId churn, three or four nested IDs that had absolutely nothing to do with what we changed. You can’t do meaningful code review on output like that. After a few weeks, people stopped actually looking at the diffs and just started rubber-stamping them.

We tried committing those exports to Git anyway, on the theory that at minimum we’d have a history. Same problem. The git log became a graveyard. Every entry looked like a complete rewrite of the workflow, even for single-character edits. The history told us nothing useful.

Then environment variables for staging and prod separation. This one worked okay, right up until someone was under deadline pressure and edited prod directly to skip the process. We did it twice in a month.

None of these had anything in common with a real deployment gate.

How do you currently handle n8n CE deployments?

0 responses

I remember sitting on a call with my manager the day after the 4:47pm incident. The conversation went exactly like this:

“Wait, why was he editing an active workflow in production anyway? Don’t we have a staging instance?”

“We do. But to deploy from staging to production, you have to manually export a JSON file from one tab and import it into another.”

“And that’s a problem because…?”

“Because it’s tedious, credentials break on import, and there’s no way to see what actually changed before you override the production workflow. Developers just started editing carefully in prod to avoid it.”

“Clearly it’s not safer. What happens if we need to roll back a bad publish?”

“We click ‘import’, pray we exported the right JSON yesterday, and hope the credentials didn’t silently vanish.”

“Wait. Are you telling me we’re running enterprise AI features for clients, and our deployment strategy is downloading a text file to your ‘Downloads’ folder?”

“Yes. Unless we want to pay €800 a month just to move a file safely.”

He stared at his webcam for a solid five seconds. “Purvesh, you’re an engineer. Stop emailing JSON files around and find a damn deployment tool.”

I couldn’t find one that fit our stack. So I spent my nights and weekends building it myself.

What I built instead

The first thing I had to figure out was the ID problem. I built the obvious version first: pull both workflow JSON objects from the API, compare their versionId fields, flag mismatches as changed. First test against our actual instance came back with half the workflows flagged as modified. I hadn’t touched any of them. n8n had just regenerated their IDs on a save somewhere. That’s actually the same thing you see when you diff raw exports by hand. The output is almost pure ID churn. An ID-based diff produces exactly the same false positives. Completely useless as a foundation. The IDs regenerate on save regardless of whether anything in the workflow actually changed. That’s n8n’s behavior, not a bug, just how it works.

SHA-256 content fingerprinting was the answer. Hash the actual workflow logic: nodes, connections, expressions, the things a developer touched. Leave the ID fields out of it entirely. When chiral diff reports a change, something in the actual workflow changed. Not n8n’s internal bookkeeping. The workflow itself.

Chiral, the n8n source-control CLI, is what came out of that. Safer production deploys so you don’t break a client’s live automation.

Try it on your own n8n setup

You'll need: n8n running, two environments set up.

chiral diff --source staging --target production
~ Updated Workflow   (modified - versionId differs)
+ New Webhook        (will be created in production)

You'll see exactly what's different between environments.

What our deployment process looks like now

After making changes in staging:

chiral diff --source staging --target production
Comparing staging → production

~ Updated Workflow         (modified - versionId differs)
+ New Webhook              (will be created - wrong name? run: chiral workflow map)

1 added, 1 modified.
Run 'chiral push --source staging --target production --dry-run' to preview.

Exact list of what changed. We review it. If it looks right:

chiral push --source staging --target production --dry-run
~ Updated Workflow         (will be updated)
+ New Webhook              (will be created)

2 changes planned.

The dry-run shows the full promotion plan: what gets pushed, how credentials remap between environments, what the git commit will look like. One more checkpoint before anything touches production. Then:

chiral push --source staging --target production
~ Updated Workflow         (updated)
+ New Webhook              (created)

✓ State synced → origin  [chore(chiral): push staging→production]

The push gets git-committed automatically. Who pushed, what changed, when. I’m building this entirely in the open because I know my team isn’t the only one hitting this CE deployment wall. The CLI is fully open-source on GitHub.

Would this have caught the 4:47pm incident?

Yes.

The in-progress workflow would have been in staging. Getting it to production would have required an explicit push. A diff would have flagged it as modified. The developer would have seen:

chiral diff --source staging --target production
Comparing staging → production

~ Content Processor v2     (modified - versionId differs)

1 modified.

That one line. That’s the whole intervention. “Oh, right. The workflow is still flagged as modified. I’m not done here.” He doesn’t push. Production never sees it. The 11 minutes don’t happen.

Are you running the same setup that broke for us?

Which n8n edition are you running?
What is the status of your workflows?
What is your team setup?

Answer the questions above to see your risk profile.

Does n8n Community Edition have staging environments?

Sort of, but not the way the question implies. You can run two separate n8n instances and call one “staging” and one “production.” Nothing stops you from doing that. The problem is getting a workflow from one to the other in a controlled way. There’s no built-in promotion path. You’re still doing the manual export/import dance, still dealing with the credential drop on import, still staring at a 200-line JSON diff that’s 90% ID noise.

Two instances doesn’t give you a deployment gate. It gives you two places to break things with no record of which one you broke or when.

n8n’s Source Control feature is the real fix for this. It’s a Business plan feature at €800/month per workspace (verified against n8n pricing, May 2026). For teams who can justify that, great. It solves the staging problem properly. Most self-hosted CE teams I’ve talked to aren’t there, which is why I built Chiral.

We got lucky. That’s the problem.

The 4:47pm incident cost us 11 minutes and some frantic clicking. The client noticed quickly, the impact was contained to one feature, and we happened to have a JSON backup recent enough to work. No data corruption, no cascading failures, no client escalation.

We were lucky. The incident that hasn’t happened yet is the one where the backup was from two days ago, or where the broken workflow corrupts stored data quietly instead of returning visible errors, or where it goes down at 11pm on a Friday and nobody notices until Monday morning. That version of this incident is a lot worse than 11 minutes.

Building the gate was a bet that we don’t find out what that version looks like.

If you’re running n8n CE to power AI workflows in production and this whole thing sounds familiar, Chiral is free to install. The free tier doesn’t expire. Teams who need rollback, RBAC, audit trails, or multi-project get those in the paid tier, but for preventing the 4:47pm incident, the free CLI is all you need.

bash
npm install -g chiral
chiral init
chiral diff --source staging --target production

First diff in about 4 minutes. You’ll see exactly what’s different between your environments, without the ID noise.

Get started with Chiral · How n8n CE source control works