Exporting and importing workflows


Exporting and importing workflows is essential for sharing automation, migrating between environments, backing up work, and collaborating with teams. This comprehensive guide covers all methods available in n8n for moving workflows between instances and users.
Understanding n8n Workflow Format
n8n stores workflows in JSON format, making them portable and human-readable. Understanding this format helps you work more effectively with exports and imports.
Workflow JSON Structure:
{
"name": "My Workflow",
"nodes": [
{
"parameters": {},
"id": "unique-node-id",
"name": "Node Name",
"type": "n8n-nodes-base.httpRequest",
"typeVersion": 1,
"position": [250, 300],
"credentials": {
"httpBasicAuth": {
"id": "credential-id",
"name": "My Credential"
}
}
}
],
"connections": {
"Node Name": {
"main": [
[
{
"node": "Next Node",
"type": "main",
"index": 0
}
]
]
}
},
"active": false,
"settings": {},
"createdAt": "2024-01-15T10:30:00.000Z",
"updatedAt": "2024-01-15T15:45:00.000Z",
"id": "workflow-id",
"tags": ["production", "api", "automation"]
}
Key Components:
Metadata: Name, ID, timestamps, tags
Nodes: Individual workflow components with parameters
Connections: How nodes are linked together
Credentials: References to authentication data
Settings: Workflow-specific configurations
Method 1: Web Interface Export/Import
The most user-friendly method for individual workflows.
Exporting via Web Interface
Single Workflow Export:
// Step-by-step export process
1. Open the workflow you want to export
2. Click the three dots menu (⋯) in the top-right corner
3. Select "Download" from the dropdown menu
4. Choose save location for the JSON file
5. File is saved as "workflow-name.json"
Partial Workflow Export (Copy-Paste):
// Export specific nodes or sections
1. Select nodes you want to export:
- Click and drag to select multiple nodes
- Hold Ctrl/Cmd and click individual nodes
- Use Ctrl+A/Cmd+A to select all nodes
2. Copy selected nodes:
- Press Ctrl+C (Windows/Linux) or Cmd+C (Mac)
- Or right-click and select "Copy"
3. Nodes are copied to clipboard as JSON
Importing via Web Interface
Import from File:
// Import workflow from local file
1. Click the three dots menu (⋯) in any workflow
2. Select "Import from File"
3. Choose your JSON workflow file
4. Click "Open" or "Import"
5. Workflow appears in your instance
Import from URL:
// Import workflow from web URL
1. Click the three dots menu (⋯)
2. Select "Import from URL"
3. Enter the URL to the JSON file
4. Example URLs:
- GitHub raw file: https://raw.githubusercontent.com/user/repo/main/workflow.json
- Direct file link: https://example.com/workflows/my-workflow.json
5. Click "Import"
Import via Copy-Paste:
// Quick import method
1. Copy JSON workflow data to clipboard
2. Open n8n workflow editor
3. Press Ctrl+V (Windows/Linux) or Cmd+V (Mac)
4. Nodes appear automatically on the canvas
Web Interface Best Practices:
Before Exporting:
// Pre-export checklist
const exportChecklist = {
"documentation": "Add workflow description and notes",
"testing": "Ensure workflow functions correctly",
"credentials": "Document required credentials",
"dependencies": "Note any external dependencies",
"tags": "Add appropriate tags for organization"
};
Security Considerations:
// Security review before sharing
const securityReview = {
"credentialNames": "Review credential names for sensitivity",
"hardcodedData": "Remove any hardcoded sensitive data",
"comments": "Check node comments for sensitive info",
"urls": "Verify URLs don't contain sensitive parameters"
};
Method 2: Command Line Interface (CLI)
The CLI provides powerful batch operations and automation capabilities.
CLI Setup and Access
Self-Hosted Installation:
# Global n8n installation
npm install -g n8n
# Verify CLI access
n8n --version
# Docker container access
docker exec -it n8n_container n8n --version
# Docker Compose access
docker-compose exec n8n n8n --version
Workflow Export Commands
Export All Workflows:
# Basic export all workflows
n8n export:workflow --all --output=./exports/
# Export with formatting and separate files
n8n export:workflow --all --pretty --separate --output=./exports/
# Export with backup settings (recommended)
n8n export:workflow --backup --output=./exports/
Export Specific Workflows:
# Export by workflow ID
n8n export:workflow --id=123 --output=./exports/
# Export multiple specific workflows
n8n export:workflow --id=123,456,789 --output=./exports/
# Export with specific naming
n8n export:workflow --id=123 --output=./exports/my-workflow.json
Advanced Export Options:
# All available export flags
n8n export:workflow \
--all \ # Export all workflows
--backup \ # Use backup settings (all, pretty, separate)
--id=123,456 \ # Specific workflow IDs
--output=./exports/ \ # Output directory
--pretty \ # Format JSON with indentation
--separate \ # Create separate files per workflow
--decrypt # Decrypt credentials (use carefully)
Credential Export Commands
Export Credentials:
# Export all credentials (encrypted)
n8n export:credentials --all --output=./exports/
# Export with backup settings
n8n export:credentials --backup --output=./exports/
# Export specific credentials
n8n export:credentials --id=cred1,cred2 --output=./exports/
# Export decrypted (SECURITY RISK - use only for migration)
n8n export:credentials --all --decrypt --output=./exports/
Import Commands
Import Workflows:
# Import from single file
n8n import:workflow --input=./exports/workflow.json
# Import all workflows from directory
n8n import:workflow --input=./exports/
# Import with separate files
n8n import:workflow --separate --input=./exports/
# Import and update IDs
n8n import:workflow --input=./exports/ --updateIds
Import Credentials:
# Import credentials
n8n import:credentials --input=./exports/credentials.json
# Import from directory
n8n import:credentials --input=./exports/
# Import with separate files
n8n import:credentials --separate --input=./exports/
CLI Automation Scripts
Complete Export Script:
#!/bin/bash
# complete-export.sh
# Configuration
EXPORT_DIR="./exports/$(date +%Y%m%d_%H%M%S)"
LOG_FILE="$EXPORT_DIR/export.log"
# Create export directory
mkdir -p "$EXPORT_DIR"
echo "Starting n8n export at $(date)" | tee "$LOG_FILE"
# Export workflows
echo "Exporting workflows..." | tee -a "$LOG_FILE"
n8n export:workflow --backup --output="$EXPORT_DIR/workflows/" 2>&1 | tee -a "$LOG_FILE"
# Export credentials
echo "Exporting credentials..." | tee -a "$LOG_FILE"
n8n export:credentials --backup --output="$EXPORT_DIR/credentials/" 2>&1 | tee -a "$LOG_FILE"
# Create manifest file
echo "Creating manifest..." | tee -a "$LOG_FILE"
cat > "$EXPORT_DIR/manifest.json" << EOF
{
"exportDate": "$(date -Iseconds)",
"n8nVersion": "$(n8n --version)",
"workflowCount": $(find "$EXPORT_DIR/workflows" -name "*.json" | wc -l),
"credentialCount": $(find "$EXPORT_DIR/credentials" -name "*.json" | wc -l)
}
EOF
echo "Export completed: $EXPORT_DIR" | tee -a "$LOG_FILE"
Selective Import Script:
#!/bin/bash
# selective-import.sh
IMPORT_DIR="$1"
WORKFLOW_FILTER="$2"
if [ -z "$IMPORT_DIR" ]; then
echo "Usage: $0 <import_directory> [workflow_filter]"
exit 1
fi
echo "Importing from: $IMPORT_DIR"
# Import credentials first
if [ -d "$IMPORT_DIR/credentials" ]; then
echo "Importing credentials..."
n8n import:credentials --input="$IMPORT_DIR/credentials/"
fi
# Import workflows
if [ -d "$IMPORT_DIR/workflows" ]; then
echo "Importing workflows..."
if [ -n "$WORKFLOW_FILTER" ]; then
# Import only matching workflows
find "$IMPORT_DIR/workflows" -name "*$WORKFLOW_FILTER*.json" -exec n8n import:workflow --input={} \;
else
# Import all workflows
n8n import:workflow --input="$IMPORT_DIR/workflows/"
fi
fi
echo "Import completed"
Method 3: REST API Integration
Programmatic export/import for advanced automation and integration scenarios.
API Authentication Setup
API Key Configuration:
// Set up API authentication
const apiConfig = {
baseUrl: 'http://localhost:5678',
apiKey: 'your-api-key-here',
headers: {
'X-N8N-API-KEY': 'your-api-key-here',
'Content-Type': 'application/json'
}
};
Workflow Export via API
Get All Workflows:
// Fetch all workflows
async function exportAllWorkflows() {
try {
const response = await fetch(`${apiConfig.baseUrl}/api/v1/workflows`, {
headers: apiConfig.headers
});
const result = await response.json();
// Save each workflow
for (const workflow of result.data) {
const filename = `workflow_${workflow.id}_${workflow.name.replace(/[^a-z0-9]/gi, '_')}.json`;
await saveWorkflowToFile(workflow, filename);
}
console.log(`Exported ${result.data.length} workflows`);
} catch (error) {
console.error('Export failed:', error);
}
}
Get Specific Workflow:
// Export specific workflow by ID
async function exportWorkflow(workflowId) {
try {
const response = await fetch(`${apiConfig.baseUrl}/api/v1/workflows/${workflowId}`, {
headers: apiConfig.headers
});
const workflow = await response.json();
const filename = `workflow_${workflow.id}_${workflow.name.replace(/[^a-z0-9]/gi, '_')}.json`;
await saveWorkflowToFile(workflow, filename);
console.log(`Exported workflow: ${workflow.name}`);
} catch (error) {
console.error('Export failed:', error);
}
}
Workflow Import via API
Create New Workflow:
// Import workflow from JSON
async function importWorkflow(workflowData) {
try {
// Remove ID to create new workflow
const { id, ...workflowWithoutId } = workflowData;
const response = await fetch(`${apiConfig.baseUrl}/api/v1/workflows`, {
method: 'POST',
headers: apiConfig.headers,
body: JSON.stringify(workflowWithoutId)
});
const result = await response.json();
console.log(`Imported workflow: ${result.name} (ID: ${result.id})`);
return result;
} catch (error) {
console.error('Import failed:', error);
}
}
Update Existing Workflow:
// Update existing workflow
async function updateWorkflow(workflowId, workflowData) {
try {
const response = await fetch(`${apiConfig.baseUrl}/api/v1/workflows/${workflowId}`, {
method: 'PUT',
headers: apiConfig.headers,
body: JSON.stringify(workflowData)
});
const result = await response.json();
console.log(`Updated workflow: ${result.name}`);
return result;
} catch (error) {
console.error('Update failed:', error);
}
}
Complete API Export/Import Class
class N8nWorkflowManager {
constructor(baseUrl, apiKey) {
this.baseUrl = baseUrl;
this.headers = {
'X-N8N-API-KEY': apiKey,
'Content-Type': 'application/json'
};
}
async exportAllWorkflows(outputDir = './exports') {
try {
const response = await fetch(`${this.baseUrl}/api/v1/workflows`, {
headers: this.headers
});
const result = await response.json();
if (!fs.existsSync(outputDir)) {
fs.mkdirSync(outputDir, { recursive: true });
}
const exportManifest = {
exportDate: new Date().toISOString(),
workflowCount: result.data.length,
workflows: []
};
for (const workflow of result.data) {
const filename = `workflow_${workflow.id}_${this.sanitizeFilename(workflow.name)}.json`;
const filepath = path.join(outputDir, filename);
fs.writeFileSync(filepath, JSON.stringify(workflow, null, 2));
exportManifest.workflows.push({
id: workflow.id,
name: workflow.name,
filename: filename,
active: workflow.active,
tags: workflow.tags
});
console.log(`Exported: ${workflow.name}`);
}
// Save manifest
fs.writeFileSync(
path.join(outputDir, 'manifest.json'),
JSON.stringify(exportManifest, null, 2)
);
console.log(`Export completed: ${result.data.length} workflows`);
return exportManifest;
} catch (error) {
console.error('Export failed:', error);
throw error;
}
}
async importWorkflowsFromDirectory(inputDir) {
try {
const manifestPath = path.join(inputDir, 'manifest.json');
let manifest = null;
if (fs.existsSync(manifestPath)) {
manifest = JSON.parse(fs.readFileSync(manifestPath, 'utf8'));
}
const workflowFiles = fs.readdirSync(inputDir)
.filter(file => file.endsWith('.json') && file !== 'manifest.json');
const importResults = [];
for (const filename of workflowFiles) {
const filepath = path.join(inputDir, filename);
const workflowData = JSON.parse(fs.readFileSync(filepath, 'utf8'));
try {
const result = await this.importWorkflow(workflowData);
importResults.push({
filename: filename,
success: true,
workflowId: result.id,
workflowName: result.name
});
console.log(`Imported: ${result.name}`);
} catch (error) {
importResults.push({
filename: filename,
success: false,
error: error.message
});
console.error(`Failed to import ${filename}:`, error.message);
}
}
console.log(`Import completed: ${importResults.filter(r => r.success).length}/${importResults.length} successful`);
return importResults;
} catch (error) {
console.error('Import failed:', error);
throw error;
}
}
async importWorkflow(workflowData) {
// Remove ID and other instance-specific fields
const { id, createdAt, updatedAt, ...cleanWorkflow } = workflowData;
const response = await fetch(`${this.baseUrl}/api/v1/workflows`, {
method: 'POST',
headers: this.headers,
body: JSON.stringify(cleanWorkflow)
});
if (!response.ok) {
throw new Error(`HTTP ${response.status}: ${response.statusText}`);
}
return await response.json();
}
sanitizeFilename(name) {
return name.replace(/[^a-z0-9]/gi, '_').toLowerCase();
}
}
// Usage example
const manager = new N8nWorkflowManager('http://localhost:5678', 'your-api-key');
// Export all workflows
await manager.exportAllWorkflows('./my-exports');
// Import workflows
await manager.importWorkflowsFromDirectory('./my-exports');
Method 4: Automated Export/Import Workflows
Create n8n workflows that export and import other workflows automatically.
Self-Exporting Workflow
// Workflow that exports all workflows to GitHub
const autoExportWorkflow = {
"name": "Auto Export Workflows",
"nodes": [
{
"name": "Schedule Trigger",
"type": "n8n-nodes-base.scheduleTrigger",
"parameters": {
"triggerInterval": "days",
"triggerAtHour": 2,
"triggerAtMinute": 0
}
},
{
"name": "Get All Workflows",
"type": "n8n-nodes-base.httpRequest",
"parameters": {
"url": "http://localhost:5678/api/v1/workflows",
"authentication": "predefinedCredentialType",
"nodeCredentialType": "n8nApi"
}
},
{
"name": "Process Each Workflow",
"type": "n8n-nodes-base.splitInBatches",
"parameters": {
"batchSize": 1
}
},
{
"name": "Format Workflow JSON",
"type": "n8n-nodes-base.set",
"parameters": {
"values": {
"string": [
{
"name": "filename",
"value": "workflow_{{ $json.id }}_{{ $json.name.replace(/[^a-z0-9]/gi, '_') }}.json"
},
{
"name": "content",
"value": "{{ JSON.stringify($json, null, 2) }}"
}
]
}
}
},
{
"name": "Commit to GitHub",
"type": "n8n-nodes-base.github",
"parameters": {
"operation": "createOrUpdateFile",
"owner": "your-username",
"repository": "n8n-workflows",
"filePath": "workflows/{{ $json.filename }}",
"fileContent": "{{ $json.content }}",
"commitMessage": "Auto-export workflow: {{ $json.name }}"
}
}
]
};
Automated Import Workflow
// Workflow that imports workflows from GitHub
const autoImportWorkflow = {
"name": "Auto Import Workflows",
"nodes": [
{
"name": "Manual Trigger",
"type": "n8n-nodes-base.manualTrigger"
},
{
"name": "List GitHub Files",
"type": "n8n-nodes-base.github",
"parameters": {
"operation": "getRepositoryContent",
"owner": "your-username",
"repository": "n8n-workflows",
"filePath": "workflows/"
}
},
{
"name": "Filter JSON Files",
"type": "n8n-nodes-base.if",
"parameters": {
"conditions": {
"string": [
{
"value1": "{{ $json.name }}",
"operation": "endsWith",
"value2": ".json"
}
]
}
}
},
{
"name": "Get File Content",
"type": "n8n-nodes-base.github",
"parameters": {
"operation": "getRepositoryContent",
"owner": "your-username",
"repository": "n8n-workflows",
"filePath": "workflows/{{ $json.name }}"
}
},
{
"name": "Parse Workflow JSON",
"type": "n8n-nodes-base.set",
"parameters": {
"values": {
"string": [
{
"name": "workflowData",
"value": "{{ JSON.parse(Buffer.from($json.content, 'base64').toString()) }}"
}
]
}
}
},
{
"name": "Import Workflow",
"type": "n8n-nodes-base.httpRequest",
"parameters": {
"url": "http://localhost:5678/api/v1/workflows",
"method": "POST",
"authentication": "predefinedCredentialType",
"nodeCredentialType": "n8nApi",
"body": "{{ $json.workflowData }}"
}
}
]
};
Migration Scenarios and Best Practices
Cross-Instance Migration
Development to Production Migration:
#!/bin/bash
# dev-to-prod-migration.sh
DEV_HOST="dev.n8n.company.com"
PROD_HOST="prod.n8n.company.com"
MIGRATION_DIR="./migration-$(date +%Y%m%d)"
echo "Starting migration from $DEV_HOST to $PROD_HOST"
# 1. Export from development
echo "Exporting from development..."
ssh $DEV_HOST "n8n export:workflow --backup --output=/tmp/migration/"
ssh $DEV_HOST "n8n export:credentials --backup --output=/tmp/migration/"
# 2. Download exports
echo "Downloading exports..."
scp -r $DEV_HOST:/tmp/migration/ $MIGRATION_DIR/
# 3. Review and sanitize
echo "Reviewing exports..."
# Add manual review step here
# 4. Upload to production
echo "Uploading to production..."
scp -r $MIGRATION_DIR/ $PROD_HOST:/tmp/
# 5. Import to production
echo "Importing to production..."
ssh $PROD_HOST "n8n import:credentials --input=/tmp/migration/"
ssh $PROD_HOST "n8n import:workflow --input=/tmp/migration/"
# 6. Cleanup
echo "Cleaning up..."
ssh $DEV_HOST "rm -rf /tmp/migration/"
ssh $PROD_HOST "rm -rf /tmp/migration/"
rm -rf $MIGRATION_DIR
echo "Migration completed"
Selective Migration
Tag-Based Migration:
// Export workflows with specific tags
async function exportByTags(tags, outputDir) {
const allWorkflows = await getAllWorkflows();
const filteredWorkflows = allWorkflows.filter(workflow =>
workflow.tags && workflow.tags.some(tag => tags.includes(tag))
);
for (const workflow of filteredWorkflows) {
const filename = `workflow_${workflow.id}_${sanitizeFilename(workflow.name)}.json`;
await saveWorkflowToFile(workflow, path.join(outputDir, filename));
}
console.log(`Exported ${filteredWorkflows.length} workflows with tags: ${tags.join(', ')}`);
}
// Usage
await exportByTags(['production', 'critical'], './prod-exports');
Version Control Integration
Git-Based Workflow Management:
#!/bin/bash
# git-workflow-sync.sh
REPO_DIR="./n8n-workflows-repo"
EXPORT_DIR="$REPO_DIR/workflows"
# Initialize or update repository
if [ ! -d "$REPO_DIR" ]; then
git clone https://github.com/company/n8n-workflows.git "$REPO_DIR"
else
cd "$REPO_DIR" && git pull origin main
fi
# Export current workflows
mkdir -p "$EXPORT_DIR"
n8n export:workflow --backup --output="$EXPORT_DIR/"
# Commit changes
cd "$REPO_DIR"
git add .
git commit -m "Automated workflow export - $(date)"
git push origin main
echo "Workflows synced to Git repository"
Security and Compliance
Credential Handling
Secure Export Practices:
// Sanitize workflows before export
function sanitizeWorkflowForExport(workflow) {
const sanitized = { ...workflow };
// Remove sensitive credential information
if (sanitized.nodes) {
sanitized.nodes = sanitized.nodes.map(node => {
const cleanNode = { ...node };
// Remove credential IDs but keep names for reference
if (cleanNode.credentials) {
Object.keys(cleanNode.credentials).forEach(credType => {
cleanNode.credentials[credType] = {
name: cleanNode.credentials[credType].name
// ID removed for security
};
});
}
// Remove sensitive parameters
if (cleanNode.parameters) {
cleanNode.parameters = removeSensitiveParameters(cleanNode.parameters);
}
return cleanNode;
});
}
return sanitized;
}
function removeSensitiveParameters(params) {
const sensitive = ['password', 'token', 'key', 'secret', 'apiKey'];
const cleaned = { ...params };
Object.keys(cleaned).forEach(key => {
if (sensitive.some(s => key.toLowerCase().includes(s))) {
cleaned[key] = '[REDACTED]';
}
});
return cleaned;
}
Audit Trail
Export/Import Logging:
// Comprehensive logging for exports/imports
class WorkflowAuditLogger {
constructor(logFile = './workflow-audit.log') {
this.logFile = logFile;
}
logExport(workflows, user, destination) {
const logEntry = {
timestamp: new Date().toISOString(),
action: 'EXPORT',
user: user,
workflowCount: workflows.length,
workflows: workflows.map(w => ({ id: w.id, name: w.name })),
destination: destination,
checksum: this.calculateChecksum(workflows)
};
this.writeLog(logEntry);
}
logImport(workflows, user, source) {
const logEntry = {
timestamp: new Date().toISOString(),
action: 'IMPORT',
user: user,
workflowCount: workflows.length,
workflows: workflows.map(w => ({ name: w.name })),
source: source,
checksum: this.calculateChecksum(workflows)
};
this.writeLog(logEntry);
}
writeLog(entry) {
const logLine = JSON.stringify(entry) + '\n';
fs.appendFileSync(this.logFile, logLine);
}
calculateChecksum(workflows) {
const crypto = require('crypto');
const content = JSON.stringify(workflows);
return crypto.createHash('sha256').update(content).digest('hex');
}
}
Troubleshooting Common Issues
Export Issues
Permission Problems:
# Fix permission issues
sudo chown -R n8n:n8n ~/.n8n/
chmod -R 755 ~/.n8n/
# For Docker
docker exec n8n chown -R node:node /home/node/.n8n/
Large Workflow Exports:
# Handle large exports with timeouts
export N8N_TIMEOUT=300000 # 5 minutes
# Split large exports
n8n export:workflow --separate --output=./exports/
Import Issues
ID Conflicts:
// Handle workflow ID conflicts during import
function resolveIdConflicts(workflowData) {
// Remove IDs to create new workflows
const { id, createdAt, updatedAt, ...cleanWorkflow } = workflowData;
// Update node IDs to prevent conflicts
if (cleanWorkflow.nodes) {
cleanWorkflow.nodes = cleanWorkflow.nodes.map(node => ({
...node,
id: generateNewId()
}));
}
return cleanWorkflow;
}
Credential Mapping:
// Map credentials during import
function mapCredentials(workflowData, credentialMapping) {
if (!workflowData.nodes) return workflowData;
const updatedNodes = workflowData.nodes.map(node => {
if (node.credentials) {
const updatedCredentials = {};
Object.keys(node.credentials).forEach(credType => {
const oldCredName = node.credentials[credType].name;
const newCredName = credentialMapping[oldCredName] || oldCredName;
updatedCredentials[credType] = {
name: newCredName
};
});
return { ...node, credentials: updatedCredentials };
}
return node;
});
return { ...workflowData, nodes: updatedNodes };
}
Performance Optimization
Batch Processing:
// Process large numbers of workflows in batches
async function batchImportWorkflows(workflowFiles, batchSize = 5) {
const batches = [];
for (let i = 0; i < workflowFiles.length; i += batchSize) {
batches.push(workflowFiles.slice(i, i + batchSize));
}
for (const batch of batches) {
const promises = batch.map(file => importWorkflowFromFile(file));
await Promise.all(promises);
// Add delay between batches to prevent overwhelming the system
await new Promise(resolve => setTimeout(resolve, 1000));
}
}
Conclusion
Exporting and importing workflows in n8n is a fundamental skill for effective automation management. Whether you're sharing workflows with colleagues, migrating between environments, or maintaining backups, understanding all available methods ensures you can handle any scenario efficiently and securely.
Key takeaways for successful workflow export/import:
Choose the Right Method: Use web interface for simple tasks, CLI for automation, API for integration
Plan Your Migration: Consider credentials, dependencies, and environment differences
Maintain Security: Sanitize sensitive data before sharing workflows
Automate When Possible: Set up automated export/import processes for regular tasks
Test Thoroughly: Always test imported workflows in the I've reached the maximum output limit for this response target environment
Document Everything: Maintain clear records of what was exported/imported and when
Handle Errors Gracefully: Implement proper error handling and recovery procedures
Advanced Export/Import Patterns
Template-Based Workflows
Creating Workflow Templates:
// Convert workflow to reusable template function createWorkflowTemplate(workflow, templateConfig) { const template = { ...workflow, name: templateConfig.templateName || `${workflow.name} Template`, id: undefined, // Remove ID for template createdAt: undefined, updatedAt: undefined, active: false, // Templates start inactive // Add template metadata templateInfo: { version: templateConfig.version || '1.0.0', description: templateConfig.description, author: templateConfig.author, category: templateConfig.category, tags: [...(workflow.tags || []), 'template'], requiredCredentials: extractRequiredCredentials(workflow), configurationSteps: templateConfig.configurationSteps || [] } }; // Parameterize common values template.nodes = template.nodes.map(node => parameterizeNode(node, templateConfig.parameters) ); return template; } function parameterizeNode(node, parameters = {}) { const parameterizedNode = { ...node }; // Replace hardcoded values with parameters Object.keys(parameters).forEach(paramKey => { const paramValue = `{{$vars.${paramKey}}}`; parameterizedNode.parameters = replaceInObject( parameterizedNode.parameters, parameters[paramKey].oldValue, paramValue ); }); return parameterizedNode; }
Template Installation Workflow:
// Workflow that installs templates with user configuration const templateInstallerWorkflow = { "name": "Template Installer", "nodes": [ { "name": "Webhook Trigger", "type": "n8n-nodes-base.webhook", "parameters": { "path": "install-template", "httpMethod": "POST" } }, { "name": "Validate Template Request", "type": "n8n-nodes-base.function", "parameters": { "functionCode": ` const { templateUrl, configuration } = $input.first().json; if (!templateUrl) { throw new Error('Template URL is required'); } return [{ json: { templateUrl, configuration: configuration || {}, installId: Date.now().toString() } }]; ` } }, { "name": "Download Template", "type": "n8n-nodes-base.httpRequest", "parameters": { "url": "={{ $json.templateUrl }}", "method": "GET" } }, { "name": "Process Template", "type": "n8n-nodes-base.function", "parameters": { "functionCode": ` const template = $input.first().json; const config = $('Validate Template Request').first().json.configuration; // Apply configuration to template const processedTemplate = applyConfiguration(template, config); return [{ json: processedTemplate }]; ` } }, { "name": "Install Workflow", "type": "n8n-nodes-base.httpRequest", "parameters": { "url": "http://localhost:5678/api/v1/workflows", "method": "POST", "authentication": "predefinedCredentialType", "nodeCredentialType": "n8nApi", "body": "={{ $json }}" } } ] };
Environment-Specific Configurations
Multi-Environment Export:
#!/bin/bash # multi-env-export.sh ENVIRONMENTS=("development" "staging" "production") BASE_DIR="./exports" DATE=$(date +%Y%m%d_%H%M%S) for env in "${ENVIRONMENTS[@]}"; do echo "Exporting from $env environment..." ENV_DIR="$BASE_DIR/$env/$DATE" mkdir -p "$ENV_DIR" # Set environment-specific connection export N8N_HOST="${env}.n8n.company.com" export N8N_PORT="5678" # Export workflows with environment tag n8n export:workflow --backup --output="$ENV_DIR/workflows/" \ --filter-tag="$env" # Export environment-specific credentials n8n export:credentials --backup --output="$ENV_DIR/credentials/" # Create environment manifest cat > "$ENV_DIR/environment.json" << EOF { "environment": "$env", "exportDate": "$(date -Iseconds)", "host": "$N8N_HOST", "workflowCount": $(find "$ENV_DIR/workflows" -name "*.json" | wc -l), "credentialCount": $(find "$ENV_DIR/credentials" -name "*.json" | wc -l) } EOF echo "Exported $env environment to $ENV_DIR" done echo "Multi-environment export completed"
Environment Configuration Mapping:
// Map configurations between environments class EnvironmentMapper { constructor(mappingConfig) { this.mappings = mappingConfig; } mapWorkflowForEnvironment(workflow, targetEnv) { const mappedWorkflow = { ...workflow }; // Update environment-specific settings mappedWorkflow.nodes = mappedWorkflow.nodes.map(node => { const mappedNode = { ...node }; // Map URLs if (mappedNode.parameters && mappedNode.parameters.url) { mappedNode.parameters.url = this.mapUrl( mappedNode.parameters.url, targetEnv ); } // Map credentials if (mappedNode.credentials) { mappedNode.credentials = this.mapCredentials( mappedNode.credentials, targetEnv ); } // Map environment variables mappedNode.parameters = this.mapParameters( mappedNode.parameters, targetEnv ); return mappedNode; }); // Update workflow tags mappedWorkflow.tags = this.updateTags(mappedWorkflow.tags, targetEnv); return mappedWorkflow; } mapUrl(url, targetEnv) { const envMappings = this.mappings.urls[targetEnv]; if (!envMappings) return url; Object.keys(envMappings).forEach(pattern => { url = url.replace(new RegExp(pattern, 'g'), envMappings[pattern]); }); return url; } mapCredentials(credentials, targetEnv) { const mappedCredentials = {}; const credMappings = this.mappings.credentials[targetEnv]; Object.keys(credentials).forEach(credType => { const originalName = credentials[credType].name; const mappedName = credMappings[originalName] || originalName; mappedCredentials[credType] = { name: mappedName }; }); return mappedCredentials; } mapParameters(parameters, targetEnv) { if (!parameters) return parameters; const mappedParams = { ...parameters }; const paramMappings = this.mappings.parameters[targetEnv]; if (paramMappings) { Object.keys(paramMappings).forEach(key => { if (mappedParams[key]) { mappedParams[key] = paramMappings[key]; } }); } return mappedParams; } updateTags(tags = [], targetEnv) { // Remove old environment tags and add new one const envTags = ['development', 'staging', 'production']; const filteredTags = tags.filter(tag => !envTags.includes(tag)); return [...filteredTags, targetEnv]; } } // Usage example const envMapper = new EnvironmentMapper({ urls: { production: { 'dev\\.api\\.': 'api.', 'staging\\.api\\.': 'api.', 'localhost:3000': 'api.company.com' }, staging: { 'dev\\.api\\.': 'staging.api.', 'localhost:3000': 'staging.api.company.com' } }, credentials: { production: { 'Dev Database': 'Production Database', 'Test API Key': 'Production API Key' }, staging: { 'Dev Database': 'Staging Database', 'Test API Key': 'Staging API Key' } }, parameters: { production: { 'debugMode': false, 'logLevel': 'error' }, staging: { 'debugMode': true, 'logLevel': 'info' } } });
Workflow Versioning and Rollback
Version Control System:
class WorkflowVersionControl { constructor(storageBackend) { this.storage = storageBackend; } async createVersion(workflowId, versionInfo) { const workflow = await this.getWorkflow(workflowId); const version = { workflowId: workflowId, version: versionInfo.version, timestamp: new Date().toISOString(), author: versionInfo.author, message: versionInfo.message, workflow: workflow, checksum: this.calculateChecksum(workflow) }; await this.storage.saveVersion(version); return version; } async listVersions(workflowId) { return await this.storage.getVersions(workflowId); } async rollbackToVersion(workflowId, versionId) { const version = await this.storage.getVersion(workflowId, versionId); if (!version) { throw new Error(`Version ${versionId} not found`); } // Create backup of current version await this.createVersion(workflowId, { version: 'pre-rollback-backup', author: 'system', message: `Backup before rollback to ${versionId}` }); // Restore the workflow await this.updateWorkflow(workflowId, version.workflow); return version; } async compareVersions(workflowId, version1, version2) { const v1 = await this.storage.getVersion(workflowId, version1); const v2 = await this.storage.getVersion(workflowId, version2); return this.generateDiff(v1.workflow, v2.workflow); } generateDiff(workflow1, workflow2) { // Simplified diff - in practice, use a proper diff library const changes = { nodes: { added: [], removed: [], modified: [] }, connections: { added: [], removed: [] }, settings: {} }; // Compare nodes const nodes1 = workflow1.nodes || []; const nodes2 = workflow2.nodes || []; const nodeIds1 = new Set(nodes1.map(n => n.id)); const nodeIds2 = new Set(nodes2.map(n => n.id)); // Find added nodes nodes2.forEach(node => { if (!nodeIds1.has(node.id)) { changes.nodes.added.push(node); } }); // Find removed nodes nodes1.forEach(node => { if (!nodeIds2.has(node.id)) { changes.nodes.removed.push(node); } }); // Find modified nodes nodes1.forEach(node1 => { const node2 = nodes2.find(n => n.id === node1.id); if (node2 && JSON.stringify(node1) !== JSON.stringify(node2)) { changes.nodes.modified.push({ before: node1, after: node2 }); } }); return changes; } calculateChecksum(workflow) { const crypto = require('crypto'); const content = JSON.stringify(workflow, null, 0); return crypto.createHash('sha256').update(content).digest('hex'); } }
Bulk Operations and Batch Processing
Bulk Export with Filtering:
// Advanced bulk export with multiple filters class BulkWorkflowExporter { constructor(apiClient) { this.api = apiClient; } async exportWithFilters(filters, outputDir) { const allWorkflows = await this.api.getAllWorkflows(); const filteredWorkflows = this.applyFilters(allWorkflows, filters); const exportManifest = { exportDate: new Date().toISOString(), filters: filters, totalWorkflows: allWorkflows.length, exportedWorkflows: filteredWorkflows.length, workflows: [] }; for (const workflow of filteredWorkflows) { const filename = this.generateFilename(workflow, filters.namingPattern); const filepath = path.join(outputDir, filename); // Apply transformations if specified const processedWorkflow = this.applyTransformations( workflow, filters.transformations ); await this.saveWorkflow(processedWorkflow, filepath); exportManifest.workflows.push({ id: workflow.id, name: workflow.name, filename: filename, tags: workflow.tags, active: workflow.active }); } // Save manifest await this.saveManifest(exportManifest, outputDir); return exportManifest; } applyFilters(workflows, filters) { let filtered = workflows; // Filter by tags if (filters.tags && filters.tags.length > 0) { filtered = filtered.filter(workflow => workflow.tags && filters.tags.some(tag => workflow.tags.includes(tag)) ); } // Filter by active status if (filters.activeOnly !== undefined) { filtered = filtered.filter(workflow => workflow.active === filters.activeOnly ); } // Filter by date range if (filters.dateRange) { const startDate = new Date(filters.dateRange.start); const endDate = new Date(filters.dateRange.end); filtered = filtered.filter(workflow => { const updatedAt = new Date(workflow.updatedAt); return updatedAt >= startDate && updatedAt <= endDate; }); } // Filter by name pattern if (filters.namePattern) { const regex = new RegExp(filters.namePattern, 'i'); filtered = filtered.filter(workflow => regex.test(workflow.name) ); } // Filter by node types if (filters.nodeTypes && filters.nodeTypes.length > 0) { filtered = filtered.filter(workflow => workflow.nodes && workflow.nodes.some(node => filters.nodeTypes.includes(node.type) ) ); } return filtered; } applyTransformations(workflow, transformations = []) { let transformed = { ...workflow }; transformations.forEach(transform => { switch (transform.type) { case 'removeCredentials': transformed = this.removeCredentialReferences(transformed); break; case 'updateTags': transformed = this.updateWorkflowTags(transformed, transform.tags); break; case 'sanitizeParameters': transformed = this.sanitizeParameters(transformed, transform.patterns); break; case 'updateUrls': transformed = this.updateUrls(transformed, transform.mappings); break; } }); return transformed; } generateFilename(workflow, pattern = 'workflow_{{id}}_{{name}}.json') { return pattern .replace('{{id}}', workflow.id) .replace('{{name}}', this.sanitizeFilename(workflow.name)) .replace('{{date}}', new Date().toISOString().split('T')[0]) .replace('{{timestamp}}', Date.now()); } sanitizeFilename(name) { return name.replace(/[^a-z0-9]/gi, '_').toLowerCase(); } } // Usage example const exporter = new BulkWorkflowExporter(apiClient); const exportResult = await exporter.exportWithFilters({ tags: ['production', 'critical'], activeOnly: true, dateRange: { start: '2024-01-01', end: '2024-12-31' }, namePattern: '^(API|Integration)', nodeTypes: ['n8n-nodes-base.httpRequest', 'n8n-nodes-base.webhook'], transformations: [ { type: 'removeCredentials' }, { type: 'updateTags', tags: { add: ['exported'], remove: ['temp'] } }, { type: 'sanitizeParameters', patterns: ['password', 'token', 'key'] } ], namingPattern: 'export_{{date}}_{{name}}.json' }, './filtered-exports');
Integration with External Systems
Slack Integration for Export/Import Notifications:
// Slack bot for workflow management const slackWorkflowBot = { "name": "Slack Workflow Manager", "nodes": [ { "name": "Slack Event Trigger", "type": "n8n-nodes-base.slackTrigger", "parameters": { "events": ["app_mention"] } }, { "name": "Parse Command", "type": "n8n-nodes-base.function", "parameters": { "functionCode": ` const text = $json.event.text; const command = text.split(' ')[1]; // First word after mention const args = text.split(' ').slice(2); // Remaining arguments return [{ json: { command: command, args: args, channel: $json.event.channel, user: $json.event.user } }]; ` } }, { "name": "Route Command", "type": "n8n-nodes-base.switch", "parameters": { "values": { "string": [ { "conditions": { "string": [ { "value1": "={{ $json.command }}", "value2": "export" } ] }, "output": 0 }, { "conditions": { "string": [ { "value1": "={{ $json.command }}", "value2": "import" } ] }, "output": 1 }, { "conditions": { "string": [ { "value1": "={{ $json.command }}", "value2": "status" } ] }, "output": 2 } ] } } }, { "name": "Handle Export", "type": "n8n-nodes-base.function", "parameters": { "functionCode": ` // Export workflows based on Slack command const tags = $json.args.filter(arg => arg.startsWith('tag:')) .map(arg => arg.replace('tag:', '')); // Trigger export workflow return [{ json: { action: 'export', filters: { tags: tags }, requestedBy: $json.user, channel: $json.channel } }]; ` } }, { "name": "Execute Export", "type": "n8n-nodes-base.executeWorkflow", "parameters": { "workflowId": "export-workflow-id" } }, { "name": "Send Export Confirmation", "type": "n8n-nodes-base.slack", "parameters": { "operation": "postMessage", "channel": "={{ $json.channel }}", "text": "✅ Export completed! {{ $json.workflowCount }} workflows exported to {{ $json.destination }}" } } ] };
Email Reports for Export/Import Activities:
// Generate and send export/import reports const reportingWorkflow = { "name": "Export Import Reporting", "nodes": [ { "name": "Schedule Trigger", "type": "n8n-nodes-base.scheduleTrigger", "parameters": { "triggerInterval": "weeks", "triggerOnWeekdays": ["monday"], "triggerAtHour": 9 } }, { "name": "Query Export Logs", "type": "n8n-nodes-base.function", "parameters": { "functionCode": ` // Read export/import logs from the past week const fs = require('fs'); const logs = fs.readFileSync('./workflow-audit.log', 'utf8') .split('\\n') .filter(line => line.trim()) .map(line => JSON.parse(line)) .filter(entry => { const entryDate = new Date(entry.timestamp); const weekAgo = new Date(); weekAgo.setDate(weekAgo.getDate() - 7); return entryDate >= weekAgo; }); return [{ json: { logs: logs } }]; ` } }, { "name": "Generate Report", "type": "n8n-nodes-base.function", "parameters": { "functionCode": ` const logs = $json.logs; const exports = logs.filter(log => log.action === 'EXPORT'); const imports = logs.filter(log => log.action === 'IMPORT'); const report = { period: 'Last 7 days', summary: { totalExports: exports.length, totalImports: imports.length, workflowsExported: exports.reduce((sum, exp) => sum + exp.workflowCount, 0), workflowsImported: imports.reduce((sum, imp) => sum + imp.workflowCount, 0) }, topUsers: getTopUsers(logs), recentActivities: logs.slice(-10) }; function getTopUsers(logs) { const userCounts = {}; logs.forEach(log => { userCounts[log.user] = (userCounts[log.user] || 0) + 1; }); return Object.entries(userCounts) .sort(([,a], [,b]) => b - a) .slice(0, 5) .map(([user, count]) => ({ user, activities: count })); } return [{ json: report }]; ` } }, { "name": "Format Email", "type": "n8n-nodes-base.function", "parameters": { "functionCode": ` const report = $json; const emailBody = \` <h2>n8n Workflow Export/Import Report</h2> <p><strong>Period:</strong> \${report.period}</p> <h3>Summary</h3> <ul> <li>Total Exports: \${report.summary.totalExports}</li> <li>Total Imports: \${report.summary.totalImports}</li> <li>Workflows Exported: \${report.summary.workflowsExported}</li> <li>Workflows Imported: \${report.summary.workflowsImported}</li> </ul> <h3>Top Users</h3> <table border="1"> <tr><th>User</th><th>Activities</th></tr> \${report.topUsers.map(user => \`<tr><td>\${user.user}</td><td>\${user.activities}</td></tr>\` ).join('')} </table> <h3>Recent Activities</h3> <ul> \${report.recentActivities.map(activity => \`<li>\${activity.timestamp}: \${activity.action} by \${activity.user} (\${activity.workflowCount} workflows)</li>\` ).join('')} </ul> \`; return [{ json: { subject: \`n8n Export/Import Report - \${report.period}\`, body: emailBody } }]; ` } }, { "name": "Send Email Report", "type": "n8n-nodes-base.emailSend", "parameters": { "to": "admin@company.com", "subject": "={{ $json.subject }}", "message": "={{ $json.body }}", "format": "html" } } ] };
Subscribe to my newsletter
Read articles from Avery Collins directly inside your inbox. Subscribe to the newsletter, and don't miss out.
Written by

Avery Collins
Avery Collins
Writing at the intersection of artificial intelligence, digital marketing, and future tech. Helping creators and startups scale with smart tools & smarter strategies. Expect weekly drops on AI use-cases, content automation, and growth experiments.