On this page 12 sections
- Key takeaways
- Who this applies to
- What the export contains, and what it does not
- Making diffs readable
- Pick a direction of truth
- A workable promotion path
- Where n8n’s own tooling fits
- What to also keep in the repository
- Why we do this on every engagement
- When this is overkill
- Frequently asked questions
- Next step
Workflows are JSON, so they belong in git like any other source. The friction is that the editor is the natural place to build and the repository is the natural place to keep things, and reconciling those two needs a deliberate promotion path. What the export does not carry is credentials, and that separation is a feature - it is also the thing that breaks a restore if you have not planned for it.
The test of whether your automation is under control: can you see what changed last Tuesday, and can you put it back?
Key takeaways
- Workflow JSON commits and diffs, which already puts n8n ahead of most automation tools.
- Credentials are never in the export. That is correct, and it means the repo is not a full backup.
- Diffs are noisy by default - node positions and ids change when nothing meaningful did.
- Decide the direction of truth: editor-first or repo-first. Ambiguity is what produces drift.
- Environment promotion needs the credential names to match, not the credentials themselves.
Who this applies to
You are running n8n with more than one person able to change workflows, or with anything in production you would not want silently modified.
What the export contains, and what it does not
A workflow export is JSON describing nodes, their parameters, their connections, and their positions on the canvas.
It does not contain credentials. Those live encrypted in the database, referenced by id and name. This is the right design - you do not want API keys in a repository - and it has two consequences people meet at the wrong moment.
Your repository is not a backup. It holds the logic and not the ability to run it. A restore needs the database as well; see self-hosted n8n for what that involves and why the encryption key matters.
Promotion between environments needs matching credential names. A workflow referencing a credential called “CRM Production” will not run in staging unless a credential of that name exists there - pointing, correctly, at the staging system. Naming credentials consistently across environments is what makes promotion work, and it is a five-minute convention that saves a recurring afternoon.
| What it is | In the workflow JSON | Where it actually lives | What breaks on restore without it |
|---|---|---|---|
| Nodes, connections, parameters | Yes | git | Nothing - this is the part that versions well |
| Node positions and UI state | Yes, and it churns | git, and it pollutes every diff | Nothing, but it hides the real change |
| Credentials | No - only a reference by id | n8n’s own encrypted store | Every external call. The workflow imports and fails on the first node |
| The encryption key | No | N8N_ENCRYPTION_KEY on the host | The credential store cannot be decrypted at all |
| Static data and execution history | No | Postgres | Deduplication state, so a re-run reprocesses everything |
| Environment variables and webhook URLs | No | The host | Triggers point at the wrong environment |
Making diffs readable
The default export produces noisy diffs. Two causes.
Node positions. Moving a node on the canvas changes its coordinates, so a purely cosmetic tidy-up produces a diff touching every node. Once workflows are in review, that trains people to skim diffs, which defeats the point.
Ids and versions. Some fields change on save without any semantic change.
The practical fix is a small normalisation step before committing: strip or round position data, sort keys deterministically, and drop volatile fields you do not care about. Twenty lines of script, run as a pre-commit hook or in the export tooling, and the diff then shows only what actually changed.
That single step is the difference between workflows being reviewed and workflows being nominally in git.
Pick a direction of truth
The decision that prevents drift, and the one most teams leave implicit.
Editor-first. People build in the n8n UI, and an export step commits the result. Comfortable, matches how the tool wants to be used, and requires discipline: an export that does not happen means the repository is stale, and a stale repository is worse than none because it looks authoritative.
Repo-first. Changes are made to files, reviewed, and deployed into n8n through the API. Rigorous, and it fights the editor - complex workflows are genuinely easier to build visually.
The hybrid that works in practice: build in the editor on a development instance, export and commit as the promotion step, and treat production as deploy-only. Production is not edited directly; changes arrive by deployment. That keeps the editor where it is useful and keeps production reproducible.
Whichever you choose, write it down and make it the same for everyone. Drift comes from two people holding different assumptions about where the real version lives.
A workable promotion path
For a team of a few people:
- Development instance. Build and iterate freely.
- Export and normalise. Script it, so the human step is one command rather than a UI dance.
- Commit with a message that says why. “Add retry to CRM sync after timeout incident” is worth more than “update workflow”.
- Review. Someone reads the normalised diff. This is where a review catches a credential pointed at the wrong environment.
- Deploy to staging via the API, with staging credentials of the same names.
- Test with real-shaped data, particularly the error paths.
- Deploy to production. Same mechanism, no manual editing.
- Tag it, so “put it back to how it was on Tuesday” is one command.
Steps 5 and 7 use n8n’s API to create or update workflows from the JSON. That is the piece worth automating early; done by hand it will be skipped under pressure.
Where n8n’s own tooling fits
n8n includes source-control functionality in its higher tiers that pushes and pulls workflows against a git repository directly. Where you have it, use it - it removes the export scripting and handles the promotion mechanics.
Check two things before relying on it: what exactly it puts in the repository, and how it handles credential references across environments. The concerns above do not disappear because the tooling is built in; they are just handled somewhere you did not write.
Where you are on a tier without it, the export-and-commit path above is straightforward and it is what most self-hosted deployments run.
What to also keep in the repository
The workflow JSON is not the whole system. Alongside it:
- The custom nodes, if any, as their own package
- The environment configuration, minus secrets
- A README naming the credentials each workflow expects and what they point at in each environment
- The error workflow, which is a workflow like any other and is frequently forgotten
- A note on the trigger - what fires this, on what schedule or from which system
That last item saves genuine time. A workflow’s JSON tells you what it does and not what causes it to run, and reconstructing that from a production instance during an incident is slow.
Why we do this on every engagement
Workflows go in the client’s repository from the first commit, with a normalisation step and API-based deployment.
The reason is handover rather than engineering aesthetics. A client whose workflows exist only inside a running n8n instance has automation they cannot review, cannot roll back, and cannot hand to another supplier without exporting by hand and hoping. That is a soft form of lock-in even when nobody intended it, and it is inconsistent with what we publish about ownership - see what you own after an AI project.
The practice that gets resisted is production being deploy-only, because editing production directly is faster when something is broken at 4pm. The honest counter: it is faster once, and the cost is that the repository silently stops matching reality, so the next rollback restores something that was never running. Where an emergency edit genuinely happens, the rule is that it gets exported and committed the same day, with a message saying it was an emergency.
Where our default is arguably too heavy: for a single-person deployment with five workflows, the full promotion path is more process than the risk justifies. Export and commit is still worth it; staging probably is not.
When this is overkill
One person, few workflows, low stakes. Export and commit occasionally. Skip the pipeline.
Genuinely experimental work. Do not put a promotion process around something you may delete next week.
n8n Cloud with one environment. The promotion path assumes environments to promote between. Version control still applies; the deployment mechanics simplify.
Frequently asked questions
Are credentials really never in the export?
Correct - only references by id and name. Which is why the repository is not a backup and the database is.
How do we handle credentials across environments?
Same names in each environment, pointing at that environment’s systems. The workflow then works unchanged wherever it is deployed.
Can we diff workflows meaningfully?
With normalisation, yes - parameter and connection changes read clearly. Without it, position noise buries the signal.
Should the error workflow be versioned too?
Yes, and it is the one most often missed because it is not attached to any particular workflow. It is production logic like anything else.
What about workflows built by non-technical staff?
Give them a development instance and make the export step somebody’s job. Do not let the answer be that some workflows are not tracked - that is precisely the set that will need a rollback.
Next step
If your production workflows exist only inside a running instance, the export-and-commit step is an afternoon and it changes what is possible during an incident. The n8n and Make engagement builds workflows under version control in your repository from the start.
Related: Self-hosted n8n · Running n8n in production · What you own after an AI project · Business process automation