--- title: N8N Export to GitHub url: https://blog.guigpap.com/en/workflows/n8n-export-github/ url_md: https://blog.guigpap.com/en/workflows/n8n-export-github.md category: tooling date: '2026-01-31' maturite: production techno: - n8n - github application: - automation - operations --- # N8N Export to GitHub > Automatic backup and versioning of N8N workflows to GitHub ## 1. What? — Definition and context The **N8N Export GitHub** workflow automatically exports modified N8N workflows to a dedicated GitHub repository. It enables versioning, easy restoration, and keeps a history of changes. > **Note - Versioning** > > **Versioning** means keeping a history of every change. If a workflow breaks after an update, you can roll back to a previous version using the Git history. ### Features | Feature | Description | |---------|-------------| | **Incremental export** | Only modified workflows are exported | | **Smart filtering** | Comparison of `updatedAt` timestamps | | **Automatic manifest** | Metadata for all workflows | | **Credential extraction** | Lists used credentials (without secrets) | | **Telegram notification** | Summary of created/modified files | | **Multiple triggers** | Schedule, manual, or webhook | ### Repository structure ``` stacks_vps_n8n_exports/ ├── README.md # Auto-generated with stats ├── manifest.json # Metadata + updatedAt ├── credentials-manifest.json # Credentials list (without secrets) └── workflows/ ├── orchestrateur-telegram.json ├── notification-hub.json └── *.json ``` --- ## 2. Why? — Stakes and motivations ### Problems solved | Problem | Without export | With export | |---------|----------------|-------------| | **Workflow loss** | N8N DB corruption = everything lost | Restore from GitHub | | **No history** | Cannot roll back | Full Git history | | **Team sync** | Manual export by email | Shared repository | | **Documentation** | Workflows not documented | Auto-generated README | ### Why an incremental export? | Approach | Advantage | Drawback | |----------|-----------|----------| | **Full export** | Simple | Useless commits, large diffs | | **Incremental export** | Relevant commits, clean history | Filtering complexity | > **Tip - Git submodule** > > The `stacks_vps_n8n_exports` repo is a submodule of the main `stacks_vps` repo. This allows versioning the exports separately while including them in the global project. --- ## 3. How? — Technical implementation ### Architecture ```mermaid flowchart TD subgraph Triggers["Triggers"] direction TB Sched["Schedule · Sunday 04:00"] Manual["Manual"] Hook["Webhook"] end GetManifest["Get Manifest from GitHub"] ListWF["List All Workflows · N8N API"] Filter{"Filter Modified · diff updatedAt"} Skip["'No changes' notification"] GetFull["Loop · Get full workflow data"] Gen["Generate JSON + README + manifest"] Push["Loop · Push to GitHub · create or update"] Notify["Telegram Notification"] Triggers --> GetManifest --> ListWF --> Filter Filter -->|no changes| Skip Filter -->|modified| GetFull --> Gen --> Push --> Notify ``` ### Triggers | Trigger | Frequency | Usage | |---------|-----------|-------| | Schedule | Sunday 4 AM | Weekly backup | | Manual | On-demand | Tests and debug | | Webhook | On-demand | Claude Code integration | **Triggering via webhook:** ```bash source /home/guillaume/stacks_vps/n8n-exports/.env curl -s -X POST "$WEBHOOK_URL" \ -H "X-Export-Token: $X_EXPORT_TOKEN" ``` ### Incremental filtering **Filter Modified (Code):** ```javascript const manifestNode = $('Get Manifest').first(); let existingMap = new Map(); if (manifestNode.json.content) { const manifest = JSON.parse( Buffer.from(manifestNode.json.content, 'base64').toString('utf8') ); existingMap = new Map( manifest.workflows.map(w => [w.id, w.updatedAt]) ); } const workflows = $input.all(); const modified = workflows.filter(wf => { const prevUpdatedAt = existingMap.get(wf.json.id); return !prevUpdatedAt || prevUpdatedAt !== wf.json.updatedAt; }); if (modified.length === 0) { return [{ json: { noChanges: true } }]; } return modified; ``` ### Generated files **manifest.json:** ```json { "exported_at": "2026-01-20T04:00:00.000Z", "workflow_count": 25, "active_count": 18, "inactive_count": 7, "workflows": [ { "id": "abc123", "name": "Orchestrateur Telegram", "active": true, "updatedAt": "2026-01-19T15:30:00.000Z" } ] } ``` **credentials-manifest.json:** ```json { "exported_at": "2026-01-20T04:00:00.000Z", "credentials": [ { "id": "1", "name": "Telegram Bot", "type": "telegramApi" }, { "id": "2", "name": "GitHub PAT", "type": "gitHubApi" } ] } ``` > **Caution - Security** > > Only credential names and types are exported, never the secrets. Workflow files contain references to credentials but not the values. ### Telegram notification ``` *N8N WORKFLOWS EXPORT* 28/01/2026 04:00 *Created (1):* - new-workflow *Updated (3):* - orchestrateur-telegram - notification-hub - manifest Unchanged: 21 [View on GitHub](https://github.com/...) ``` ### Required credentials | Credential | Type | Usage | |------------|------|-------| | `n8n account` | n8n API | List/Get workflows | | `PAT (classic)` | GitHub API | Get/Create/Edit files | | `Telegram Bot` | Telegram API | Notifications | --- ## 4. What if? — Outlook and limits ### Current limits | Limit | Impact | Mitigation | |-------|--------|------------| | **No automatic restore** | Manual import in N8N | Restore script planned | | **Credentials not exported** | Manually recreate after restore | Only names are saved | | **Git history only** | No one-click rollback | GitHub UI for diffs | ### Evolution scenarios **If automatic restore is needed**: - Create a restore-from-GitHub workflow - Selection of workflow and version - Import via the N8N API **If multiple N8N instances**: - Adapt the manifest per instance - Bidirectional sync between instances - Conflict handling **If workflow volume grows**: - Per-category export (active only) - Compression of older exports - Git retention policy ### Useful commands ```bash # View recent exports ls -la n8n-exports/workflows/ # View local changes cd n8n-exports && git status # Compare with a previous version git diff HEAD~1 workflows/orchestrateur-telegram.json # Restore a workflow # 1. Copy the JSON from GitHub # 2. N8N → Import workflow → Paste JSON ``` ### Troubleshooting | Problem | Check | |---------|-------| | No export | Workflow active? Cron correct? | | 401 GitHub | Valid PAT? Repo permissions? | | Constant "No changes" | manifest.json corrupted? Delete and re-run | | Duplicate files | Verify `updatedAt` filtering | --- ## Related pages ### Workflows - [Notification Hub](/en/workflows/notification-hub/) — Telegram notifications - [Telegram Orchestrator](/en/workflows/telegram-orchestrator/) — Manual trigger ### Infrastructure - [N8N Queue Mode](/en/infrastructure/n8n-queue-mode/) — Backend automation ### References - [stacks_vps_n8n_exports](https://github.com/GuiGPaP/stacks_vps_n8n_exports) — Exports repository ## Metadonnees agent - Cet article est issu du blog GuiGPaP Lab. - Contexte global du blog: https://blog.guigpap.com/llms.txt - Contact auteur: https://odoo.guigpap.com/mon-cv - Licence: CC-BY-SA 4.0