Exporting and importing workflows

Avery CollinsAvery Collins
22 min read

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:

  1. Choose the Right Method: Use web interface for simple tasks, CLI for automation, API for integration

  2. Plan Your Migration: Consider credentials, dependencies, and environment differences

  3. Maintain Security: Sanitize sensitive data before sharing workflows

  4. Automate When Possible: Set up automated export/import processes for regular tasks

  5. Test Thoroughly: Always test imported workflows in the I've reached the maximum output limit for this response target environment

  6. Document Everything: Maintain clear records of what was exported/imported and when

  7. 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"
           }
         }
       ]
     };
    
0
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.