Backup n8n Workflows to a Gitea Repository
Picture this: you spend hours crafting the perfect n8n workflow, everything runs smoothly, you feel like an automation wizard… then one day you tweak a node, hit save, and suddenly nothing works. You do the classic “Ctrl+Z stare” at the screen, but there is nothing to undo. That sinking feeling? Completely optional, if you have proper backups.
This guide walks you through an n8n workflow template that automatically exports all your workflows and backs them up to a Gitea Git repository. It checks if each workflow file already exists, creates it if needed, or updates it when things change, all on a recurring schedule. In other words, it quietly does the boring stuff in the background so you never have to.
Why bother backing up n8n workflows to Gitea?
Because “I’ll remember what I changed” is not a backup strategy.
Automating your n8n workflow backups gives you:
- Recovery after mistakes – roll back from configuration errors, accidental deletes, or “I just wanted to try something” incidents.
- Self-hosted control – Gitea is a lightweight, self-hosted Git service, so you control storage, privacy, and retention.
- Version history – each workflow is stored in Git, so you get proper versioned backups using standard Git semantics.
- API-friendly automation – the Gitea contents API plays nicely with n8n, so the whole thing runs via simple HTTP calls.
Combine n8n with Gitea and you get a neat little system that quietly tracks how your workflows evolve over time, without you manually exporting JSON files like it is 2009.
What this n8n backup template actually does
This workflow template handles the full loop of “export from n8n, sync to Gitea, repeat on a schedule”. At a high level, it works like this:
- Schedule trigger runs periodically, for example every 45 minutes, so backups happen on autopilot.
- Globals node stores reusable variables like
repo.url,repo.name, andrepo.ownerso you do not hardcode URLs everywhere. - n8n (API) node fetches the list of workflows from your n8n instance.
- ForEach (split) node loops through each workflow one by one.
- GetGitea (HTTP) node checks if a JSON file for that workflow already exists in the Gitea repository.
- Exist (IF) node decides whether the file exists and routes to the correct path.
- Base64EncodeCreate / Base64EncodeUpdate (Code) nodes pretty-print each workflow as JSON and encode it in base64, exactly how the Gitea contents API expects it.
- PostGitea (HTTP) node creates a new file in the repo if it does not exist yet.
- PutGitea (HTTP) node updates an existing file when the workflow has changed, using the file SHA returned by Gitea.
The result: every workflow in your n8n instance ends up as a separate JSON file in your Gitea repository, automatically kept up to date.
Before you start: what you need in place
Before you hit “Execute workflow” with wild optimism, make sure you have:
- An n8n instance with permission to call the n8n API or otherwise retrieve workflows.
- A Gitea instance and repository ready to store backups, for example a repo named
workflows. - A Gitea Personal Access Token with repository read and write permissions.
- Basic familiarity with n8n nodes and credentials configuration, so you can plug in tokens and URLs without guesswork.
How the workflow decides when to create or update files
The template is not just blindly overwriting files. It is a bit smarter than that:
- GetGitea tries to fetch
{workflow-name}.jsonfrom the configured Gitea repository using the contents API. - If Gitea returns a 404, the file does not exist. The Exist IF node sends the item down the “create” branch, where:
- The workflow JSON is pretty-printed and base64 encoded in the Base64EncodeCreate Code node.
- PostGitea creates a brand-new file in the repo.
- If the file exists, the template:
- Pretty-prints and base64-encodes the current workflow JSON in the Base64EncodeUpdate Code node.
- Compares this with the
contentfield returned by Gitea (after decoding). - The Changed IF node checks if the content is different. If it is, PutGitea updates the file and includes the file’s
shato create a new commit.
So only changed workflows generate new commits, and unchanged ones are left alone. Your Git history stays clean instead of becoming a wall of identical “no-op” commits.
Encoding details: why all the base64?
Gitea’s contents API expects file contents in base64, not plain JSON. The template handles this with small Python Code nodes that:
- Take the workflow JSON payload from the incoming item.
- Serialize it as pretty-printed JSON, with indentation that is easy for humans to read in the repo.
- Convert the string to UTF-8 bytes and then base64 encode it.
- Return an object with an
itemfield containing that base64 string, which the HTTP POST and PUT nodes send to Gitea.
You get readable JSON in Git, and Gitea gets exactly the format its API expects. Everyone is happy.
Step-by-step setup guide
Now for the fun part: turning this from “nice idea” into “actually running”. Here is how to configure the template in your n8n instance.
1. Set up global repository variables
Open the Globals node and fill in your Gitea details:
repo.url– for examplehttps://git.your-domain.comrepo.name– for exampleworkflowsrepo.owner– the user or organization that owns the repository
These variables keep the rest of the workflow clean, so if you ever move the repo or change the URL, you only update it in one place.
2. Create and configure your Gitea token
- In Gitea, go to Settings → Applications → Generate Token and create a Personal Access Token with repository read and write permissions.
- In the n8n credentials manager, create a new HTTP Header credential for Gitea with:
Header name: Authorization Header value: Bearer YOUR_PERSONAL_ACCESS_TOKEN
Make sure there is a space after Bearer, or Gitea will reject the request and silently judge you with 401s.
3. Attach the token to the HTTP nodes
Open each of the HTTP nodes in the workflow and assign the Gitea Token credential you just created:
GetGitea– checks if a workflow file already exists.PostGitea– creates new files for workflows that are not yet in the repo.PutGitea– updates existing files when workflows change.
Once this is done, n8n can talk to Gitea without you manually copying tokens into every request.
4. Configure the n8n API node
Find the node named n8n in the template. This is the API node that retrieves your workflows. Make sure it has the right authentication to list all workflows in your instance.
Depending on how you run n8n, this might use:
- A built-in n8n credential type, or
- A generic HTTP or token-based credential that can call the n8n API.
Once it can enumerate workflows, you are ready to test the whole backup process.
Testing the backup workflow
Before you trust this with your entire automation empire, give it a quick test run:
- Configure the n8n API node to use a single test workflow first, then run the workflow manually.
- Open your Gitea repository and confirm that a new file appears, usually named something like
<workflow-name>.json, with the expected JSON content and commit message. - Make a small change to the test workflow in n8n, then run the backup workflow again. Check Gitea to verify that:
- The existing file was updated, not duplicated.
- A new commit was created for the change.
Once you are happy with the results, you can switch the API node back to backing up all workflows and let the schedule trigger handle it automatically.
Security tips and troubleshooting
Keep your repo and tokens safe
- Use a private repository for backups so your workflows are not publicly visible.
- Rotate Personal Access Tokens regularly and keep scopes as minimal as possible.
- Prefer an organization-owned repository for team setups so backups are not tied to a single user account.
Common issues and how to fix them
- HTTP 401 or 403 from Gitea
Check the Authorization header format and confirm the token has repo read and write permissions. - 404 from GetGitea
This is normal when a file does not exist yet and is the expected path for creating new files. If it happens unexpectedly, verify the file path and ensure names are properly URL encoded, for example usingencodeURIComponent. - Base64 comparison mismatches
Make sure both the template and Gitea use the same canonical formatting. The template already pretty-prints the JSON before encoding so the comparison is consistent. - Very large workflows timing out
For huge workflows, you might need to increase the request timeout on the HTTP nodes or adjust how you handle particularly large content.
Example Gitea request bodies
For reference, here is roughly what the workflow sends to Gitea.
Creating a file with POST:
{ "content": "BASE64_ENCODED_JSON", "message": "Add workflow: <workflow-name>"
}
Updating a file with PUT (including SHA):
{ "content": "BASE64_ENCODED_JSON", "sha": "FILE_SHA", "message": "Update workflow: <workflow-name>"
}
The sha comes from the previous GetGitea response and tells Gitea which version you are updating, so it can create a new commit.
Ideas for extending the template
Once the basic backup is running smoothly, you can level it up a bit:
- Add richer commit messages that include workflow ID, version, or timestamps.
- Create tags or branches for nightly or weekly snapshots.
- Send notifications via email or Slack when a backup fails or when a new commit is created.
- Implement retention logic to prune old backups after a certain period.
The template already covers the essentials, so extensions like these are just icing on top.
Wrapping up: turn “oops” into “no problem”
Backing up n8n workflows to a Gitea repository turns your fragile, single-copy workflows into versioned, auditable, and recoverable assets. This template gives you:
- Scheduled exports of all workflows.
- Smart create vs update logic using the Gitea contents API.
- Correct base64 encoding of pretty-printed JSON.
- Clean credential handling for secure API access.
Import the template into your n8n instance, update the Globals node with your repo details, attach your Gitea Token credential to the HTTP nodes, and activate the schedule trigger. Run a few manual tests, then let automation take over the repetitive backup work.
If you want to go further, you can build on this to commit entire repo trees or switch to Git over SSH instead of the REST API. And if you ever find yourself breaking a workflow on a Friday afternoon, you will be very glad this is quietly running in the background.
