I Lost a Working n8n Workflow to a Bad Edit — Now Git Versions Every Deploy
I Lost a Working n8n Workflow to a Bad Edit — Now Git Versions Every Deploy Part of my build-in-public series on running LLM workflows in n8n in production. This one is about the infrastructure nobody brags about: vers
I Lost a Working n8n Workflow to a Bad Edit — Now Git Versions Every Deploy
Part of my build-in-public series on running LLM workflows in n8n in production. This one is about the infrastructure nobody brags about: version control. n8n has no git. I added it myself — a nightly exporter that snapshots every workflow into a git repo, so a bad edit is a git checkout instead of a 40-minute guessing game.
The edit that cost me a Friday night
A few months ago I made a "small" improvement to a client's inbox triage workflow: I rewrote the classify prompt to be "clearer." Clearer, it turned out, also meant longer, and the extra instructions pushed the model to hedge — every email came back "uncertain," the router dumped everything into the review queue, and the client woke up to 200 un-triaged emails.
The workflow had been perfect on Thursday. By Friday morning I couldn't tell you exactly what Thursday's prompt said. n8n keeps a version history per workflow, but scrolling through "version 47" and "version 46" at 1 a.m., trying to diff prompts in my head, is not a recovery strategy. It's a prayer. It took me 40 minutes to reconstruct what I'd changed, and I only managed it because I'd pasted the old prompt into a Slack message to myself two weeks earlier — pure luck.
That was the last time a workflow existed only inside n8n.
The exporter: 5 nodes, one cron, every workflow in git
That weekend I built a meta-workflow — a workflow whose only job is to back up all the other workflows. It runs every night at 11 p.m. and does exactly this:
- Cron trigger — 23:00 daily. Boring on purpose.
-
Get all workflows — an HTTP node hitting my own n8n instance's API (
GET /api/v1/workflows) with an API key. The exporter backs itself up too; I've seen enough backup scripts that exclude themselves. -
Strip the noise — a Code node that removes the volatile fields from each workflow JSON before saving:
updatedAt,createdAt,versionId, and the per-executionwebhookIdchurn. Without this step, every nightly export shows "changes" that are just timestamps, and the git history becomes unreadable. This is the step everyone skips and then wonders why their repo has 400 meaningless commits. -
Write files — each workflow lands in the repo as
workflows/<slug>.json, plus amanifest.jsonwith workflow name, node count, active status, and last editor. One workflow, one file, so diffs are readable. -
Commit only on change — a shell/Execute node runs
git diff --quietfirst. If nothing changed, no commit. If something changed, it commits with a message likenightly: inbox-triage, prompt changed in classify nodeand pushes. I get a Telegram ping only when there's an actual change, with the--statsummary so I can see "2 files changed, 14 insertions" from my phone.
The whole repo is private — workflow JSON contains prompt IP and client-specific logic. But it's real git: branches, tags for "known good" states, and git log --oneline as my deploy history.
Restore is the part that matters (and the part I test)
Backups you never restore are decoration. Mine get restored — monthly, on purpose. The drill: pick a workflow, check out last week's version, import it into a staging n8n instance, run it against a test inbox, confirm it behaves like last week. The drill has caught two real problems this year:
- Import loses webhook URLs. When you import a workflow JSON, n8n regenerates the webhook path. My exporter now records the production webhook URL in the manifest, and the restore procedure includes a manual re-registration step. The drill taught me this, not the docs.
- Sub-workflow references break. An imported workflow that calls another workflow by ID will point at the wrong thing in a fresh instance, because IDs differ. My convention now: sub-workflows are referenced by name via an Execute Workflow node lookup, never by hardcoded ID. The exporter validates this on every run — any workflow with a raw sub-workflow ID in it fails the nightly check and pings me.
The honest number: the drill takes 20 minutes a month. The Friday-night incident took 40 minutes and a client's trust. The math isn't close.
Three rules that keep the repo honest
Rule 1: the repo is the referee, not the editor. I still edit workflows in the n8n UI — git is a snapshot layer, not a development workflow. Nobody hand-edits the JSON files; the exporter overwrites them. This avoids the classic trap of two sources of truth. If a file in the repo and the live workflow disagree, the live workflow wins and the next nightly run reconciles them with a commit. The repo records what shipped, nothing more.
Rule 2: credentials never enter the repo. The n8n API export doesn't include credentials by default, and I keep it that way — deliberately. The manifest records which credentials each workflow uses (by name), but never the secrets. A fresh-instance restore means re-entering credentials by hand. Annoying? Slightly. Safer than the alternative by a mile.
Rule 3: n8n upgrades get their own commit. When n8n releases a new version, the workflow JSON schema sometimes shifts, and the next nightly export shows a diff on every single file. I commit that as schema-bump: n8n 1.x → 1.y so the noise has a name and a date. Six months later, when you're wondering why 30 files changed on the same night, you'll thank past-you.
The failure mode I'm still watching
One thing the exporter can't fix: two humans editing the same workflow. Git merge conflicts in workflow JSON are genuinely awful — a thousand-line JSON blob where the "conflict" is two people moving the same node. My answer right now is structural, not technical: I'm the only editor. The manifest records a single owner per workflow, and client handoffs include one rule — you tell me what to change, I change it, git records it. It works at my scale. It would not survive a team of five, and I'm honest about that: the day I hire a second automation engineer, this becomes a real problem, and the answer is probably per-workflow locking, not better merges.
The takeaway for n8n builders
If you run more than three workflows in production, build the exporter before you need it — which is to say, build it this weekend. The machinery is small: one API call, one noise-stripping function, git diff --quiet, a cron. The discipline is smaller: never commit noise, never store secrets, restore on a schedule. n8n won't give you version control. But it gives you an API, and an API plus git is version control with extra steps — and extra steps you only notice on the nights when everything breaks.
I'm building AI Automation Lab — production-ready n8n workflow templates, plus fixed-price custom builds (from $79) via the Contact page. The canonical pipeline in this series ships as the AI Inbox Triage & Reply Drafter ($29); the free AI Hook Generator is the zero-risk way to see how I build. My open-source patterns live at github.com/zhp910318/n8n-ai-automation-lab.
Originally published by Dev.to AI. Aggregated on AIWithGhost for educational purposes — full credit and traffic to the original publisher.