# n8n Workflow Backup & GitOps - Version History (`mediocre_interest/n8n-workflow-backup`) Actor

Back up an entire n8n instance on a schedule - every workflow, tag, variable, project, credential reference, folder, data table and community package. One row per resource change with a node-level diff, full JSON kept in a permanent store, and an optional commit pushed to your own Git repo.

- **URL**: https://apify.com/mediocre\_interest/n8n-workflow-backup.md
- **Developed by:** [Mediocre\_Interest](https://apify.com/mediocre_interest) (community)
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $4.50 / 1,000 resources backed ups

This Actor is paid per event. You are not charged for the Apify platform usage, but only a fixed price for specific events.
Since this Actor supports Apify Store discounts, the price gets lower the higher subscription plan you have.

Learn more: https://docs.apify.com/actors/running/actors-in-store.md#pay-per-event

## What's an Apify Actor?

Actors are web data automations that power AI and operations. They run on the Apify platform to scrape websites, process data, connect APIs, and automate workflows.
In Batch mode, an Actor accepts a well-defined JSON input, performs an action which can take anything from a few seconds to a few hours,
and optionally produces a well-defined JSON output, datasets with results, or files in key-value store.
In Standby mode, an Actor provides a web server which can be used as a website, API, or an MCP server.
Actors are written with capital "A".

## How to integrate an Actor?

If asked about integration, you help developers integrate Actors into their projects.
You adapt to their stack and deliver integrations that are safe, well-documented, and production-ready.
The best way to integrate Actors is as follows.

- **AI agents and MCP clients** — the [Apify MCP server](https://docs.apify.com/integrations/mcp.md) at `https://mcp.apify.com` (remote, streamable HTTP, OAuth on first use).
- **Agentic workflows and local Actor development** — [Agent Skills](https://apify.com/.well-known/agent-skills/index.json) with the [Apify CLI](https://docs.apify.com/cli/docs.md): `npm install -g apify-cli`, then `apify login`.
- **JavaScript/TypeScript projects** — the official [JS/TS client](https://docs.apify.com/api/client/js/docs.md): `npm install apify-client`.
- **Python projects** — the official [Python client](https://docs.apify.com/api/client/python/docs.md): `pip install apify-client`.
- **Any other language** — the [REST API](https://docs.apify.com/api/v2.md).

For usage examples, see the [API](#api) section below.

For more details, see Apify documentation as [Markdown index](https://docs.apify.com/llms.txt) and [Markdown full-text](https://docs.apify.com/llms-full.txt).

# README

## n8n Workflow Backup & GitOps — Version History

**Back up an entire n8n instance on a schedule and keep a full version history.** Each run writes one
row per resource saying what changed since last time — added, modified, removed or archived, with
node-level counts for workflows — stores the complete JSON in a permanent store, and can push a
commit to your own Git repository.

Community and Starter plans give you 24 hours of workflow history and no Git at all. This gives you
unlimited history and a commit in a repository you own.

- **Whole instance, not just workflows.** Tags, variables, projects, credential references, folders,
  data tables and installed community packages are versioned too.
- **One row per change.** A renamed tag and a rewired workflow both get a row, with a plain-English
  summary of what moved.
- **Your own Git repo.** GitHub or GitLab over HTTPS, with a commit only when something actually
  changed.
- **Read-only.** The Actor never writes to your n8n instance.

### What does this Actor do?

Every run:

- Fetches every workflow from your instance, plus tags, variables, projects, credential references,
  folders, data tables and installed community packages.
- Compares each of them against the previous run and records what changed.
- Saves the full JSON, and keeps a dated copy whenever anything changes.
- Optionally commits the whole backup to a GitHub or GitLab repository.

### How do I back up my n8n instance?

1. In n8n, go to **Settings → n8n API** and create an API key.
2. Paste your instance URL and the key into this Actor's input.
3. Click **Start** — or add a **Schedule** and run it daily, which is what it is built for.
4. When the run finishes, open the **Output** tab for the change log and the run summary.

Leave both fields empty to see a demo run against built-in sample data first.

#### Creating an n8n API key

1. In n8n, select your avatar, then **Settings**.
2. Open **n8n API** in the left menu and select **Create an API key**.
3. Give it a **Label** and an **Expiration**.
4. Copy the key — n8n shows it only once — and paste it into **n8n API key** here, with your
   instance address in **n8n instance URL**.

Two things worth knowing before you set an expiry: the key stops working the day it expires and
your scheduled backups start failing, so either pick a long expiry or set yourself a reminder. And
if you rebuild the n8n instance, the old key dies with it — a run that suddenly fails with
`rejected the API key (401)` almost always means the key belongs to an instance that no longer
exists.

On **Enterprise** plans the key also offers **Scopes**: this Actor needs `workflow:list` and
`workflow:read`, plus the matching `list` scope for each other collection you want backed up. Every
other plan has no scope picker and the key carries full access, so there is nothing to choose.

### Input

Ten fields, all optional. Only the instance URL and the API key are needed to back up your own n8n.

| Field             | Type    | Default               | What it does                                      |
| ----------------- | ------- | --------------------- | ------------------------------------------------- |
| `n8nBaseUrl`      | string  | —                     | Instance URL. Empty with an empty key runs a demo |
| `n8nApiKey`       | secret  | —                     | From **Settings → n8n API**                       |
| `include`         | array   | all eight             | Which collections to back up                      |
| `workflowIds`     | array   | `[]`                  | Back up only these workflows. Empty means all     |
| `activeOnly`      | boolean | `false`               | Back up only workflows that are switched on       |
| `backupStoreName` | string  | `n8n-workflow-backup` | Where history is kept. One per instance           |
| `gitRemoteUrl`    | string  | —                     | HTTPS repository URL. Empty skips Git             |
| `gitToken`        | secret  | —                     | Token with write access to that repository        |
| `gitBranch`       | string  | `main`                | Branch to commit to                               |
| `gitPath`         | string  | `n8n/`                | Directory inside the repository. `/` for the root |

A run filtered with `workflowIds` or `activeOnly` never reports a workflow as removed.

#### Input example

```json
{
    "n8nBaseUrl": "https://n8n.example.com",
    "n8nApiKey": "<your n8n public API key>",
    "gitRemoteUrl": "https://github.com/you/n8n-backups",
    "gitToken": "<token with write access>",
    "gitBranch": "main",
    "gitPath": "n8n/"
}
```

### What data do I get back?

#### The change log — one row per resource per run

**Default dataset.** The **Type** column says whether a row is a workflow, a tag, a credential and so
on; **What changed** carries a plain-English summary.

```json
{
    "runAt": "2026-09-08T05:10:55.785Z",
    "resourceType": "workflow",
    "resourceId": "demo-onboarding-01",
    "name": "Customer onboarding email",
    "changeType": "added",
    "diffSummary": "New workflow with 2 nodes",
    "active": true,
    "isArchived": false,
    "nodeCount": 2,
    "nodesAdded": 2,
    "nodesRemoved": 0,
    "nodesModified": 0,
    "tags": "production",
    "updatedAt": "2026-02-11T14:22:00.000Z",
    "secretsFound": 0,
    "snapshotUrl": "https://api.apify.com/v2/key-value-stores/<id>/records/workflow-demo-onboarding-01"
}
```

- **`changeType`** is `added`, `modified`, `removed`, `archived`, `backfilled` or `unchanged`.
- **`secretsFound`** counts values in a workflow that look like hardcoded credentials, without
  revealing any of them.
- **`snapshotUrl`** links to the full JSON for that resource in the backup store.

#### The backup store

The current JSON of everything, plus a dated copy of every past version. It is kept indefinitely, and
its link is in the run summary and the run log.

#### The run summary

**Key-value store — `summary-run`**: totals, which collections were skipped and why, the backup
store's location, and the Git commit if one was pushed.

### How do I get the backup into Git?

Fill in the **Git sync** section: an HTTPS repository URL, an access token with write permission, and
optionally a branch and a directory. Each run pushes a single commit describing the changes, and
makes no commit at all when nothing changed.

**GitHub and GitLab are both supported**, including GitLab subgroups. Paste the repository address
from your browser — `https://github.com/you/n8n-backups`, `https://gitlab.com/you/n8n-backups`, or
the page you happen to be looking at — and the URL is tidied up for you. SSH remotes (`git@…`) are
not supported.

#### Creating a GitHub token

A **fine-grained token** is the narrower option and the one to prefer:

1. In the upper-right corner of any GitHub page, select your profile picture, then **Settings**.
2. In the left sidebar, select **Developer settings**.
3. Under **Personal access tokens**, select **Fine-grained tokens**, then **Generate new token**.
4. Set a **Token name** and an **Expiration**.
5. Under **Resource owner**, pick the account or organisation that owns your backup repository.
6. Under **Repository access**, choose **Only select repositories** and pick that repository.
7. Under **Permissions** → **Repository permissions**, set **Contents** to **Read and write**.
8. Select **Generate token** and copy it.

A **classic token** also works: same first two steps, then **Tokens (classic)** → **Generate new
token (classic)**, and tick the **`repo`** scope. It is much broader — a classic token can push to
every repository you have access to — so prefer a fine-grained one.

If the repository belongs to an organisation, a fine-grained token may need an organisation owner to
approve it before it can push.

#### Creating a GitLab token

1. In the upper-right corner, select your avatar, then **Edit profile**.
2. In the left sidebar, select **Access** → **Personal access tokens**.
3. From the **Generate token** dropdown, select **Legacy token**.
4. Set a **Token name** and an **Expiration date**.
5. Tick the **`write_repository`** scope. It grants pull and push over HTTPS, which is all this
   Actor needs — it does not grant API access.
6. Select **Generate token** and copy it.

A **project** or **group** access token is narrower and works the same way: go to that project or
group → **Settings** → **Access tokens**, give it the **Developer** role and the same
`write_repository` scope.

An empty repository is the easiest place to start — the first run initialises it and creates the
branch. Anything outside the directory you choose is left alone, so pointing this at a repository
that already has a README or other files is safe. To write to the repository root, enter a single
`/`.

If someone else pushes while a run is in flight, that run's push is refused and says so. The backup
is already saved, and the next run picks up from the new commit.

The repository mirrors the backup store, so you can diff any workflow in your provider's own UI:

```
n8n/workflows/<id>-<name>.json
n8n/tags.json  variables.json  projects.json  credentials.json
n8n/folders.json  datatables.json  community-packages.json
n8n/manifest.json
```

**Variable values are never committed**, and credential secrets are never fetched at all. What
reaches the repository, and what stays private to your Apify account, is set out in
[What is stored, and what should I know before sharing it?](#what-is-stored-and-what-should-i-know-before-sharing-it)

### Do I need a paid n8n plan?

No. Workflows, tags, credential references and community packages work on every plan, including
self-hosted Community. Variables, Projects and Folders are paid n8n features — if your plan or your
API key does not include them, they are skipped and noted in the run summary, and the rest of the
backup proceeds normally. A skipped collection keeps whatever the last successful run stored.

The n8n public API is unavailable during the n8n Cloud free trial.

### Can I back up more than one instance?

Yes — give each instance its own **Backup store name**. If a store already holds a different
instance's history, the run stops rather than overwriting it.

### What is stored, and what should I know before sharing it?

Your backup lands in two places, and they do not hold the same things. The backup store is private
to your Apify account; the Git repository is as public as you choose to make it.

|                                                       | Backup store      | Git repository                  |
| ----------------------------------------------------- | ----------------- | ------------------------------- |
| Workflow JSON                                         | Complete          | Complete                        |
| Credential names and types                            | Yes               | Yes                             |
| **Credential secrets**                                | **Never fetched** | **Never fetched**               |
| **Variable values**                                   | Real values       | **Blanked — key and type only** |
| Tags, projects, folders, data table schemas, packages | Yes               | Yes                             |
| User accounts                                         | Not backed up     | Not backed up                   |

**The Actor only ever reads from n8n.** It never creates, edits, activates or deletes anything on
your instance.

**A complete workflow JSON includes anything hardcoded inside it.** That is what makes the backup
restorable, but it means an authentication header pasted in from a cURL import is in there too.
Treat the backup store's URL like the workflows themselves.

**Credential secrets are never fetched.** n8n's public API returns each credential as a name and a
type with no secret value, and only the name and type are backed up.

**Variable values never reach Git.** `variables.json` in the repository lists each variable's key and
type with the value blanked. The real values stay in the backup store, so a restore still has them
and a repository you later make public does not.

**The change log holds no workflow content** — counts, names and hashes only. Its **Possible
secrets** column tells you how many values in a workflow look like hardcoded credentials, without
revealing any of them.

**Your API key and Git token are stored encrypted** by Apify, and never appear in the log, in a row,
or in the repository.

**History is never deleted.** Every changed version is kept, so the backup store grows over time and
is yours to trim.

#### What is not backed up

- **Pinned node data** — a restored workflow comes back without its pins.
- **`staticData`** — execution state n8n rewrites as workflows run and rebuilds by itself.
- **Data table rows** — each table's name and columns are kept, the rows are not.
- **User accounts** — a user record restores nothing and would put email addresses in your
  repository.

### How much does it cost?

Pay per event: one charge per **resource** backed up, and one per Git commit pushed. A resource is
anything the Actor versions — a workflow, a tag, a variable, a project, a credential reference, a
folder, a data table or a community package — each billed at the same rate. Current per-event prices
are on this Actor's Store page.

Unchanged resources are charged for too; a verified unchanged copy is what a backup is for.

If your Apify credit does not cover the whole instance, the run backs up as much as it can afford and
keeps it, skips the Git commit so your repository never holds a partial instance, and finishes as
failed. Add credit and re-run to collect the rest.

### Integrations and API access

- **Use it from n8n.** Apify's [official n8n integration](https://docs.apify.com/platform/integrations/n8n)
  lets an n8n workflow run this Actor — so n8n can back itself up.
- **Schedule it.** Add a schedule to keep a daily history of your instance.
- **Call it from the API.** Start runs and fetch results with the [Apify API](https://docs.apify.com/api/v2),
  or the [JavaScript](https://docs.apify.com/sdk/js/) and [Python](https://docs.apify.com/sdk/python/) clients.
- **Pipe it anywhere.** Webhooks, Zapier, Make, Slack, Google Sheets, and the rest of the
  [Apify integrations](https://docs.apify.com/platform/integrations).

Back up an instance and get the change log back in a single call:

```sh
curl -X POST "https://api.apify.com/v2/acts/mediocre_interest~n8n-workflow-backup/run-sync-get-dataset-items?token=$APIFY_TOKEN" \
  -H 'Content-Type: application/json' \
  -d '{"n8nBaseUrl":"https://n8n.example.com","n8nApiKey":"<your n8n public API key>"}'
```

The response is a JSON array of the change-log rows shown above. Send `{}` instead to run the demo on
built-in sample data. Add `&format=csv` for a spreadsheet, or `&fields=resourceType,name,changeType,diffSummary`
to trim the columns.

### What other Actors work with this one?

| Actor                                                                                                     | What it does                                                                                                                                                           |
| --------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| [n8n Workflow Auditor](https://apify.com/mediocre_interest/n8n-workflow-auditor)                          | Lints n8n workflow JSON for hardcoded credentials, unauthenticated webhooks, deprecated nodes and missing error handling.                                              |
| [n8n Node Catalog & Community Package Scraper](https://apify.com/mediocre_interest/n8n-node-catalog)      | Catalogs every n8n node — built-in, verified, and community — with npm provenance, license, weekly downloads, and maintenance risk for each package.                   |
| [n8n Workflow Builder — AI Generator from Text](https://apify.com/mediocre_interest/n8n-workflow-builder) | Describe an automation in plain English and get n8n workflow JSON that imports and runs, with every node type and parameter checked against n8n's live node catalogue. |

The auditor is the natural companion: this Actor tells you **what changed**, and the auditor tells you
whether what changed is **safe**. A `secretsFound` above zero on any row here is exactly what to paste
into it.

### FAQ

#### Does this change anything in my n8n instance?

No. The Actor only reads. It never creates, edits, activates or deletes anything on your instance.

#### Can it see my credentials?

No. n8n's public API returns credentials as names and types without their secret values, and only
those are backed up. See
[What is stored](#what-is-stored-and-what-should-i-know-before-sharing-it) for the full picture.

#### Can it restore a backup for me?

Not yet — this Actor backs up and versions, it does not write back to n8n. What it stores is the
complete workflow JSON, so you can import any version into n8n yourself from the editor or the public
API.

#### Why does every row say `unchanged`?

Because nothing on the instance changed since the last run, which is the normal result for a daily
backup. Unchanged rows are still written and still charged: a verified unchanged copy is the point of
a backup. When nothing has changed, no Git commit is made either.

#### Does it work with self-hosted n8n?

Yes, as long as the Actor can reach the instance over HTTPS and the public API is enabled. Workflows,
tags, credential references and community packages work on Community; Variables, Projects and Folders
need a paid n8n plan.

#### What happens if a run is interrupted?

Everything backed up before the interruption is kept — nothing is rolled back, and you are only ever
charged for what was actually stored. No Git commit is pushed by a run that did not finish, so the
repository never holds a partial instance. Re-run to collect the rest.

#### Can I back up only some workflows?

Yes — list them in **Workflow IDs**, or switch on **Activated workflows only**. A filtered run never
reports a workflow as removed, since it cannot tell an excluded workflow from a deleted one.

### Support

Found a bug, or need a field that isn't here? Open an issue on the **Issues** tab of this Actor.

When reporting a problem with a backup, include the run ID and the resource type and ID from the row
you are asking about.

# Actor input Schema

## `n8nBaseUrl` (type: `string`):

Your n8n instance, for example https://example.app.n8n.cloud. You can paste any n8n page URL and the address will be trimmed for you. Leave this and the API key empty to run a demo on built-in sample data.

## `n8nApiKey` (type: `string`):

Create one in n8n under Settings -> n8n API. Leave this and the instance URL empty to run a demo on built-in sample data.

## `include` (type: `array`):

Which parts of the instance to back up.

## `workflowIds` (type: `array`):

Back up only these workflow IDs. Leave empty to back up every workflow.

## `activeOnly` (type: `boolean`):

Back up only workflows that are switched on.

## `backupStoreName` (type: `string`):

Name of the key-value store holding your snapshots and version history. Use a different name for each n8n instance you back up.

## `gitRemoteUrl` (type: `string`):

HTTPS URL of the repository to commit backups to - for example https://github.com/you/n8n-backups or https://gitlab.com/you/n8n-backups. You can paste the repository page from your browser and the address is tidied up for you. GitHub, GitLab and GitLab subgroups all work, and an empty repository is fine - the first run initialises it and creates the branch. SSH remotes (git@...) are not supported. Leave empty to skip Git.

## `gitToken` (type: `string`):

Token allowed to push to that repository. On GitHub, a fine-grained token with Contents: read and write, or a classic token with the "repo" scope. On GitLab, a personal, group or project access token with the "write\_repository" scope. It is stored as a secret and never appears in the output.

## `gitBranch` (type: `string`):

Branch to commit to. It is created by the first run if the repository is new and empty.

## `gitPath` (type: `string`):

Directory inside the repository the backup is written into, for example n8n/. Enter a single "/" to write to the repository root. Anything outside this directory is left alone, so an existing repository with a README or other files is safe.

## Actor input object example

```json
{
  "include": [
    "workflows",
    "tags",
    "variables",
    "projects",
    "credentialMetadata",
    "folders",
    "dataTables",
    "communityPackages"
  ],
  "workflowIds": [],
  "activeOnly": false,
  "backupStoreName": "n8n-workflow-backup",
  "gitBranch": "main",
  "gitPath": "n8n/"
}
```

# Actor output Schema

## `changes` (type: `string`):

No description

## `runSummary` (type: `string`):

No description

# API

You can run this Actor programmatically using our API. Below are code examples in JavaScript, Python, and CLI, as well as the OpenAPI specification and MCP server setup.

## JavaScript example

```javascript
import { ApifyClient } from 'apify-client';

// Initialize the ApifyClient with your Apify API token
// Replace the '<YOUR_API_TOKEN>' with your token
const client = new ApifyClient({
    token: '<YOUR_API_TOKEN>',
});

// Prepare Actor input
const input = {};

// Run the Actor and wait for it to finish
const run = await client.actor("mediocre_interest/n8n-workflow-backup").call(input);

// Fetch and print Actor results from the run's dataset (if any)
console.log('Results from dataset');
console.log(`💾 Check your data here: https://console.apify.com/storage/datasets/${run.defaultDatasetId}`);
const { items } = await client.dataset(run.defaultDatasetId).listItems();
items.forEach((item) => {
    console.dir(item);
});

// 📚 Want to learn more 📖? Go to → https://docs.apify.com/api/client/js/docs

```

## Python example

```python
from apify_client import ApifyClient

# Initialize the ApifyClient with your Apify API token
# Replace '<YOUR_API_TOKEN>' with your token.
client = ApifyClient("<YOUR_API_TOKEN>")

# Prepare the Actor input
run_input = {}

# Run the Actor and wait for it to finish
run = client.actor("mediocre_interest/n8n-workflow-backup").call(run_input=run_input)

# Fetch and print Actor results from the run's dataset (if there are any)
print(f"💾 Check your data here: https://console.apify.com/storage/datasets/{run.default_dataset_id}")
for item in client.dataset(run.default_dataset_id).iterate_items():
    print(item)

# 📚 Want to learn more 📖? Go to → https://docs.apify.com/api/client/python/docs/quick-start

```

## CLI example

```bash
echo '{}' |
apify call mediocre_interest/n8n-workflow-backup --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,mediocre_interest/n8n-workflow-backup"
        }
    }
}
```

The hosted server signs you in with OAuth on first connect, so no API token belongs in this config. Clients without OAuth support can send an `Authorization: Bearer <APIFY_API_TOKEN>` header instead, using a token from API & Integrations in Apify Console (https://console.apify.com/settings/integrations).

## OpenAPI specification

Download the OpenAPI definition: https://api.apify.com/v2/actors/9rAPNZawGytFl5Opa/builds/PfjyoX28Ioxculkar/openapi.json
