
n8n Version Control Without an Enterprise Plan
n8n's built-in Git source control is a Business and Enterprise feature, and Business is roughly €800 a month. On Community, Starter or Pro you can rebuild most of it for free with the public REST API: a scheduled job that pulls every workflow, strips the volatile fields, and commits one JSON file per workflow to Git. You get history, readable diffs, pull request review and rollback. You don't get credentials, and you don't get a pull button.
What n8n's Paid Source Control Actually Buys You
It's a real feature and it works. You link an instance to a Git repo, push selected workflows plus tags and variables, and pull them into another instance. Branch per environment, dev on one, prod on another. If you have a team of five people touching workflows every day, it earns its money.
Two things about it are worth knowing before you assume it's the answer. First, n8n's own documentation is refreshingly honest that you shouldn't view n8n's source control as full version control — there's no pull-request review inside the product, unless you go do it in GitHub yourself. Second, pulling overwrites. If you edited a workflow in the UI and didn't push it, a pull eats your work.
The part that decides it for most people:
Source control and environments sit on the Business and Enterprise plans. Business lists around €800/month at the time of writing. For a solo builder or a small agency, that's a lot of money for a feature that still won't review a pull request.
I run client automations on self-hosted n8n. When I priced the jump, the honest question was: what am I actually buying? Backups, diffs, and a promotion path from dev to prod. All three are reachable through an API that ships on every plan.
The Free Version: Public API Plus a Cron Job
Create a key in Settings → n8n API, then talk to /api/v1 with an X-N8N-API-KEY header. Listing workflows is paginated by cursor, so the whole export is a small loop:
#!/usr/bin/env bash
set -euo pipefail
: "${N8N_URL:?}" "${N8N_API_KEY:?}"
cursor=""; mkdir -p workflows
while :; do
url="$N8N_URL/api/v1/workflows?limit=250"
[ -n "$cursor" ] && url="$url&cursor=$cursor"
page=$(curl -sf -H "X-N8N-API-KEY: $N8N_API_KEY" "$url")
echo "$page" | jq -c '.data[]' | while read -r wf; do
id=$(jq -r '.id' <<<"$wf")
nm=$(jq -r '.name' <<<"$wf" | tr '[:upper:] ' '[:lower:]-' \
| tr -cd 'a-z0-9-')
jq -S 'del(.updatedAt, .versionId, .pinData, .meta.instanceId)
| .nodes |= sort_by(.name)' <<<"$wf" \
> "workflows/${id}--${nm}.json"
done
cursor=$(echo "$page" | jq -r '.nextCursor // empty')
[ -z "$cursor" ] && break
donePoint it at a Git repo, run it from cron or a GitHub Action, and commit only when git diff --quiet fails. Mine runs nightly and most nights it commits nothing, which is exactly what you want — the commit log becomes a list of days someone actually changed something.
Why Your First Attempt Produces a Diff Every Single Night
This is the step every tutorial skips, and it's the difference between a backup and version control. Dump the API response straight to disk and every export looks changed, because the payload carries fields that move on their own:
- updatedAt and versionId — n8n bumps these on activation, on execution settings changes, on things you never touched.
- pinData — your pinned test fixtures, which have nothing to do with production behavior.
- meta.instanceId — a fingerprint of the instance, guaranteed to differ between dev and prod.
- Node order and canvas positions — dragging a node two pixels is a diff unless you sort and ignore.
The jq -S in the script above sorts keys, the del(...) drops the volatile ones, and .nodes |= sort_by(.name) makes node order stable. After normalizing, a commit diff reads like a sentence: “model changed from claude-sonnet-5 to claude-opus-5, maxIterations 100 → 8.” Before normalizing, it's four hundred changed lines and you stop reading them, which means you stop noticing when something real slips in.
If you'd rather not build the retrieval half at all, n8n added workflow version-history endpoints to the public API — you can list a workflow's versions and fetch a specific one. Handy for a targeted rollback. It still isn't Git: no branches, no review, no diff across workflows, and the history lives inside the instance you're trying to protect.
Credentials: The Part Git Can't Save You From
Here's the limitation people discover during a restore, which is the worst possible moment. The credentials endpoint says it outright: credential data is not included. You get IDs, names, types and sharing — never the secret.
That's the correct design and it's also the same on the paid feature, which pushes credential stubs to Git and makes you refill them after a pull. Nobody is escaping this. What you can do is make the gap explicit instead of discovering it under pressure:
curl -sf -H "X-N8N-API-KEY: $N8N_API_KEY" \
"$N8N_URL/api/v1/credentials?limit=250" \
| jq -S '[.data[] | {id, name, type}]' \
> credentials-inventory.jsonCommit that file. It's your restore checklist: twelve credentials, here are their names and types, go re-enter them from the password manager. A restore that takes forty minutes of re-authentication is fine. A restore where you don't know what you're missing is an outage.
One security note, because it bites people: scoped API keys are an Enterprise feature. On every other plan the key you just created can read and write everything on the instance. Keep it in a secret store, give it an expiry, and don't paste it into a workflow node — the same discipline that applies to authenticating a remote MCP server.
Restoring Is a PUT, Not a Pull
Going the other way — Git back into an instance — is where the DIY route asks for real work. Update an existing workflow with PUT /api/v1/workflows/{id}, create a missing one with a POST, then activate it. Strip the read-only fields first or the API rejects the body.
The trap is credential IDs. A workflow references credentials by ID, and those IDs are per-instance. Push a dev workflow into prod verbatim and it points at credential IDs that either don't exist there or, worse, exist and belong to something else. Keep a small map.jsonof dev ID → prod ID and run the JSON through it during promotion. It's twenty lines of code and it's the difference between a deploy script and a Friday afternoon incident.
A restore you have never run is a hope, not a backup. Mine gets tested against a throwaway Docker instance once a quarter — same habit as verifying you can actually recover a database, and the same reason I keep traces on the agent workflows rather than trusting the execution list.
When You Should Just Pay for It
| Built-in source control | API + Git | |
|---|---|---|
| Plan | Business / Enterprise | Any, including Community |
| Setup | SSH key + repo, ~30 min | A script + a cron, half a day |
| Pull into instance | One click (overwrites local) | You write the PUT + ID mapping |
| PR review | Not in n8n | Yes — it's a normal repo |
| Credentials | Stubs only | Inventory only |
| Who it fits | Teams, RBAC, audit requirements | Solo builders, small agencies |
Pay when more than two people edit workflows on the same instance, when you need environments with role-based access, or when someone external has to see that the control exists. Governance is genuinely hard to fake with a cron job, and if you're already at the scale where queue mode is on the table, you're probably at the scale where the plan makes sense.
Below that, the free route wins on more than price. Your workflows sit in a normal repository, so they get branches, pull requests, blame, and CI that can lint a workflow before it merges — none of which the paid feature does inside n8n.
The Short Version
- n8n's Git source control is a Business and Enterprise feature. Business is around €800/month.
- The public REST API ships on every plan, including Community. That's all you need for backups and diffs.
- Loop GET /api/v1/workflows with the cursor, write one JSON file per workflow, commit only when something changed.
- Normalize first: drop updatedAt, versionId, pinData and meta.instanceId, sort keys and node order. Otherwise every night is a fake diff.
- Credentials are never exported by anyone — paid or free. Commit the inventory as a restore checklist and keep secrets in a vault.
- Restoring is a PUT plus an activate call, and you need a dev→prod credential ID map or you will point prod at the wrong account.
- On non-Enterprise plans API keys are unscoped. Treat one like a root password.
- Pay for the built-in feature when you have a team, RBAC needs, or an auditor. Otherwise a repo gives you PR review that the paid feature doesn't.
New to running n8n seriously? Start with the n8n workflow tutorial, then make the workflows survive their own failures with proper control flow around agent errors. The two official pages worth bookmarking are n8n's source control docs and the public API authentication reference.
Running Client Workflows With No Backup?
I build and maintain self-hosted n8n setups that come with the boring parts attached — Git-backed workflow exports, a restore path that's actually been tested, and a deploy script instead of copy-paste between instances. If your automations only exist in one Postgres database right now, that's a fast fix.
Related Posts
n8n
How to Trace an n8n AI Agent with Langfuse
n8n's execution view shows what every node received and returned. That is not a trace. A trace is one searchable timeline where a single agent run, its tool calls, its retries and its token cost sit under one parent span — so you can ask which prompt version caused last Tuesday's bad answer. Two ways to get there: an HTTP Request node posting to the Langfuse ingestion API, which works on n8n Cloud and survives upgrades, or OpenTelemetry instrumentation of the self-hosted n8n process, which gives you a span per node but is a community proof of concept built on n8n internals. Start with the HTTP Request node, use $execution.id as the trace ID, and pick Langfuse over LangSmith because n8n never exposes the LangChain callbacks LangSmith needs and Langfuse charges nothing per seat.
n8n
How to Evaluate n8n AI Agents Before Production
An n8n AI agent evaluation is four pieces: a dataset of real test cases in a Data Table or Google Sheet, an Evaluation Trigger node that replays every row through the live workflow, an Evaluation node that scores the answer, and a threshold you refuse to ship below. n8n gives you five built-in metrics — Correctness and Helpfulness are LLM-judged on a 1–5 scale, while String Similarity, Categorization, and Tools Used are deterministic and effectively free — plus custom metrics from a Code node. The critical detail is testing the real workflow, not a copy: the Check If Evaluating operation branches side effects out of a test run so nothing emails a real customer. Track two metrics per agent, baseline before you tune, and turn every production incident into a test case the same day.
n8n
Scaling Self-Hosted n8n: When to Switch to Queue Mode (2026)
Default n8n runs the editor, webhooks, and every execution in one Node process — it works until the UI crawls during runs and webhooks drop under load. The signal to move is the main process pinned near 80% CPU; the fix is queue mode: a main instance, a Redis broker, and dedicated workers on Postgres. The exact signals I watch, the env vars I set, and the mistakes that cost me a night of dropped executions.