n8n Has No Version Control: Build a Backup and Rollback System
Quick Answer
n8n has no version control for workflows — every save permanently overwrites the previous state, with no branches, no commit history, and no built-in rollback. Build your own by capturing a snapshot of the workflow’s JSON via n8n’s API before every meaningful change, storing it with a timestamp, and having a rollback workflow that can push a stored snapshot back through the same API on demand.
The Version Control Gap
n8n stores each workflow in your database as a single JSON object. When you click “Save,” the platform overwrites that record entirely. There’s no diff, no history of what changed between versions, and no way to see what the workflow looked like an hour ago short of restoring an entire database backup.
The execution history n8n does keep tracks runs, not configuration changes — you can see that an execution failed, but not what the workflow looked like before the edit that caused it to start failing. A full database dump can technically get you back to an earlier state, but it’s a blunt instrument: it rolls back everything in the database to that point in time, not just the one workflow you actually need reverted.
Traditional software development solved this decades ago with version control: every change is a commit, and reverting to a known-good state takes seconds. n8n workflows are business logic that changes just as often as application code, and they don’t get the same protection by default.
This isn’t a hypothetical edge case. Any workflow that’s actively maintained — new nodes added, credentials rotated, mappings adjusted as an upstream API changes — eventually gets an edit that breaks something. Without your own backup system, “roll back to what worked yesterday” isn’t an option n8n gives you; you’re reconstructing the previous configuration from memory, screenshots, or whatever you can piece together.
Build a Workflow Versioning System
This uses n8n’s own REST API and a webhook trigger to capture a snapshot of a workflow’s JSON before you touch it, and a companion workflow to push a stored snapshot back if the change breaks something.
Step 1: Create the Backup Workflow
A workflow triggered by webhook that fetches the current state of a target workflow and stores it:
{
"nodes": [
{
"name": "Webhook Trigger",
"type": "n8n-nodes-base.webhook",
"parameters": {
"path": "backup-workflow",
"httpMethod": "POST"
}
},
{
"name": "Get Workflow Data",
"type": "n8n-nodes-base.httpRequest",
"parameters": {
"url": "={{$parameter[\"n8n_base_url\"]}}/api/v1/workflows/{{$json[\"workflow_id\"]}}",
"authentication": "predefinedCredentialType",
"nodeCredentialType": "n8nApi"
}
},
{
"name": "Create Backup Record",
"type": "n8n-nodes-base.postgres",
"parameters": {
"operation": "insert",
"table": "workflow_backups",
"columns": "workflow_id, backup_data, created_at, version_tag",
"additionalFields": {
"workflow_id": "={{$json[\"id\"]}}",
"backup_data": "={{JSON.stringify($json)}}",
"created_at": "={{new Date().toISOString()}}",
"version_tag": "={{$parameter[\"version_tag\"] || 'auto-backup'}}"
}
}
}
]
}
Step 2: Database Schema for Version Storage
CREATE TABLE workflow_backups (
id SERIAL PRIMARY KEY,
workflow_id VARCHAR(50) NOT NULL,
backup_data JSONB NOT NULL,
created_at TIMESTAMP DEFAULT NOW(),
version_tag VARCHAR(100),
is_working BOOLEAN DEFAULT true
);
CREATE INDEX idx_workflow_backups_id_date ON workflow_backups(workflow_id, created_at DESC);
Step 3: Trigger a Backup Before Every Change
Call the backup webhook from wherever your change process starts — a deploy script, a pre-edit checklist, or manually before opening the editor:
// Run before editing any workflow you'd need to recover
const backupUrl = 'https://your-n8n-instance.com/webhook/backup-workflow';
const workflowId = $json.id;
await fetch(backupUrl, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
workflow_id: workflowId,
version_tag: `pre-update-${new Date().toISOString()}`
})
});
Step 4: Build the Rollback Workflow
The restoration side: look up a stored snapshot by workflow ID and version tag, then push it back through the API.
{
"nodes": [
{
"name": "Rollback Trigger",
"type": "n8n-nodes-base.webhook",
"parameters": {
"path": "rollback-workflow",
"httpMethod": "POST"
}
},
{
"name": "Get Backup Version",
"type": "n8n-nodes-base.postgres",
"parameters": {
"operation": "select",
"table": "workflow_backups",
"where": {
"workflow_id": "={{$json[\"workflow_id\"]}}",
"version_tag": "={{$json[\"version_tag\"]}}"
}
}
},
{
"name": "Restore Workflow",
"type": "n8n-nodes-base.httpRequest",
"parameters": {
"url": "={{$parameter[\"n8n_base_url\"]}}/api/v1/workflows/{{$json[\"workflow_id\"]}}",
"method": "PUT",
"body": "={{JSON.parse($json[\"backup_data\"])}}",
"authentication": "predefinedCredentialType",
"nodeCredentialType": "n8nApi"
}
}
]
}
Trigger a rollback the same way you triggered the backup — a webhook call with the workflow_id and the version_tag you want to restore:
curl -X POST https://your-n8n-instance.com/webhook/rollback-workflow \
-H "Content-Type: application/json" \
-d '{"workflow_id": "123", "version_tag": "pre-update-2026-07-28T14:00:00.000Z"}'
What This Doesn’t Solve
This is a manual-trigger backup system, not real version control. It only protects you if you actually call the backup webhook before making a change — it won’t catch edits made without going through that step. It also doesn’t diff versions for you; you’re restoring a full prior state, not merging changes. For workflows where that matters, treat this as a floor, not a complete solution: pair it with a habit (or a hook) that makes the pre-change backup automatic rather than something you have to remember.
Related Reading
See Automation Governance for the broader discipline this fits into — ownership, change control, and promotion for production automation generally, not just the backup mechanics covered here.


