n8n's built-in workflow history is useful for undoing your own last few edits, but it is not version control — it doesn't survive an instance migration, it can't tell you what changed between two dates six months apart, and it gives you nothing to diff in a pull request. If more than one person touches your workflows, or if you run separate staging and production n8n instances, you need the workflows in git like any other artifact, with the same review and rollback discipline as application code.
Exporting workflow JSON to git
n8n workflows are JSON under the hood, and the CLI can export and import them without going through the UI. n8n export:workflow --all --output=./workflows/ writes one JSON file per workflow, which you commit to a repo. The awkward part is that the exported JSON contains n8n-generated IDs, position coordinates for the canvas, and sometimes stale credential references — so a git diff on a workflow you barely touched can show a large, noisy change. Pin down a formatting convention (many teams run the export through a small script that strips volatile fields like node position and pins them to a fixed value) so diffs actually reflect logic changes, not canvas jitter.
n8n export:workflow --all --output=./workflows/ --pretty
git add workflows/
git commit -m "sync: export workflows from n8n instance"
git push origin main
# re-importing on another instance (e.g. staging -> prod promotion)
n8n import:workflow --input=./workflows/invoice-sync.json
Credentials never belong in the export
n8n workflow exports reference credentials by ID, not by value — the actual API keys and OAuth tokens live in n8n's encrypted credential store, separate from the workflow JSON. That's good news for git hygiene: a workflow export is safe to commit. The catch is that credential IDs are instance-specific, so a workflow exported from staging and imported into production will point at a credential ID that doesn't exist there, or worse, silently binds to the wrong credential if IDs happen to collide. Recreate credentials per environment first, note their IDs, and treat the credential mapping as part of your deployment runbook, not something n8n solves automatically.
Some older n8n export paths and third-party backup scripts pulled full credential payloads for portability. Don't commit those to a repo, even a private one — treat any file containing decrypted credential values the same as a secrets leak, and rotate immediately if one lands in git history.
Staging vs. production environments
Running two n8n instances — staging and production — is the only reliable way to test a workflow change before it touches real customer data. The pattern that works: staging and production each have their own credential sets pointing at sandbox vs. live third-party accounts (Stripe test keys vs. live keys is the obvious example), workflows are promoted by importing the git-tracked JSON rather than manually rebuilding, and environment-specific values (webhook URLs, base API URLs) are pulled from n8n environment variables rather than hardcoded into node parameters.
{
"parameters": {
"url": "={{ $env.API_BASE_URL }}/v1/invoices",
"authentication": "predefinedCredentialType",
"nodeCredentialType": "stripeApi"
},
"name": "Fetch Invoice",
"type": "n8n-nodes-base.httpRequest"
}
What git cannot catch for you
Version control gets you diffs and rollback for the workflow's logic, but it doesn't validate that a workflow will actually run correctly against a different environment's data shape. A webhook payload from a sandbox payment provider can differ subtly from the live payload — extra fields, different casing, a nullable field that's never actually null in test data. Treat a git-tracked workflow the same way you'd treat a merged pull request: it still needs a manual or scripted smoke test against the target environment before you trust it in production.
If a promoted workflow misbehaves in production, the fastest fix is re-importing the previous git-tagged JSON, not trying to manually undo changes in the n8n editor. Tag or branch each production promotion so "roll back to last week's version" is a one-line git checkout plus one import command.
Wrapping up
n8n's native versioning is fine for undoing your own recent mistakes; it is not a substitute for git. Export workflow JSON on a schedule or as part of your deploy process, keep credentials out of the exported files by relying on n8n's credential-ID indirection, and treat environment promotion as an explicit step with its own credential mapping — not something that happens automatically just because the JSON imported cleanly.
Independent software engineer in Nairobi specialising in Acumatica customisations, Laravel backends, and tax fiscalisation integrations across East and Southern Africa.