portainer-mcp/README_GITOPS.md
Adolfo Delorenzo f9d8407c09 docs: add references to individual server documentation files
- Add documentation links in main README overview section
- Create new Documentation section listing all README files
- Ensure all README files are properly referenced
- All server documentation is now easily discoverable

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-07-19 01:05:33 -03:00

15 KiB

GitOps Implementation Fix for Portainer MCP Servers

Problem

The Portainer MCP servers were unable to enable GitOps on existing Git-based stacks. The enable_stack_gitops function in portainer_gitops_server.py was failing with "Invalid stack file content" errors.

Root Cause

The Portainer API's stack update endpoint (PUT /api/stacks/{stack_id}) has limitations:

  1. It requires StackFileContent to be included in update requests
  2. When StackFileContent is included, it converts Git-based stacks to file-based stacks
  3. This sets IsDetachedFromGit: true and clears the GitConfig

Solution

Enable GitOps during stack creation rather than as a separate step. The API supports including autoUpdate configuration when creating stacks from Git repositories.

Implementation Changes

Updated portainer_stacks_server.py

Added GitOps parameters to the create_compose_stack_from_git tool:

# New parameters in the tool schema:
"enable_gitops": {
    "type": "boolean",
    "description": "Enable GitOps automatic updates",
    "default": False
},
"gitops_interval": {
    "type": "string",
    "description": "GitOps polling interval (e.g., '5m', '1h')",
    "default": "5m"
},
"gitops_mechanism": {
    "type": "string",
    "enum": ["polling", "webhook"],
    "description": "GitOps update mechanism",
    "default": "polling"
},
"gitops_pull_image": {
    "type": "boolean",
    "description": "Pull latest images on GitOps update",
    "default": True
},
"gitops_force_update": {
    "type": "boolean",
    "description": "Force redeployment even if no changes",
    "default": False
}

The implementation now adds autoUpdate to the request when creating stacks:

if arguments.get("enable_gitops", False):
    auto_update = {
        "interval": arguments.get("gitops_interval", "5m"),
        "forcePullImage": arguments.get("gitops_pull_image", True),
        "forceUpdate": arguments.get("gitops_force_update", False)
    }
    
    if arguments.get("gitops_mechanism") == "webhook":
        auto_update["webhook"] = arguments.get("gitops_webhook_id", "")
    
    data["autoUpdate"] = auto_update

Usage

Creating a Stack with GitOps Enabled

# Using the MCP server
await tool.call("create_compose_stack_from_git", {
    "environment_id": "6",
    "name": "nginx-gitops",
    "repository_url": "https://github.com/example/repo",
    "repository_ref": "main",
    "compose_path": "docker-compose.yml",
    "enable_gitops": True,
    "gitops_interval": "5m",
    "gitops_mechanism": "polling",
    "gitops_pull_image": True
})

Direct API Call

curl -X POST "https://portainer.example.com/api/stacks/create/standalone/repository?endpointId=6" \
  -H "X-API-Key: your-api-key" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "nginx-gitops",
    "repositoryURL": "https://github.com/example/repo",
    "repositoryReferenceName": "main",
    "composeFilePathInRepository": "docker-compose.yml",
    "repositoryAuthentication": false,
    "autoUpdate": {
      "interval": "5m",
      "forcePullImage": true,
      "forceUpdate": false
    }
  }'

Alternative Approaches

If you need to enable GitOps on existing stacks:

  1. Delete and Recreate: Delete the existing stack and recreate it with GitOps enabled
  2. Use Webhooks: Configure webhooks at the Git repository level
  3. External Automation: Use external tools to trigger stack updates via the Git redeploy endpoint

Testing

Use the provided test_gitops_create.py script to verify GitOps functionality:

python test_gitops_create.py

This will:

  1. Create a new stack with GitOps enabled
  2. Verify the GitOps configuration
  3. Display the stack details including AutoUpdate settings

Limitations

  • Cannot enable GitOps on existing Git-based stacks without detaching them from Git
  • Cannot update GitOps settings (like polling interval) on existing stacks without detaching them from Git
  • The Portainer API does not provide a way to update Git-based stacks while maintaining Git connection
  • Any update that includes StackFileContent converts the stack from Git-based to file-based
  • This appears to be a fundamental limitation in Portainer's API design

Why You Cannot Update GitOps Interval

When attempting to update the GitOps interval (e.g., from 5m to 3m) on an existing stack:

  1. API Limitation: The stack update endpoint (PUT /api/stacks/{id}) requires StackFileContent
  2. Side Effect: Including StackFileContent in the update request:
    • Sets IsDetachedFromGit: true
    • Clears GitConfig: null
    • Clears AutoUpdate: null
  3. No Partial Updates: The API doesn't support updating only the AutoUpdate configuration

Workaround

To change the GitOps interval, you must:

  1. Delete the existing stack
  2. Recreate it with the new interval

Example:

# Delete existing stack
curl -X DELETE "https://portainer.example.com/api/stacks/{stack_id}?endpointId={env_id}" \
  -H "X-API-Key: your-api-key"

# Recreate with new interval
curl -X POST "https://portainer.example.com/api/stacks/create/standalone/repository?endpointId={env_id}" \
  -H "X-API-Key: your-api-key" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "stack-name",
    "repositoryURL": "https://github.com/example/repo",
    "repositoryReferenceName": "refs/heads/main",
    "composeFilePathInRepository": "docker-compose.yml",
    "autoUpdate": {
      "interval": "3m",
      "forcePullImage": true,
      "forceUpdate": false
    }
  }'

Recommendations

  1. Always enable GitOps during stack creation if needed
  2. Consider opening a feature request with Portainer for enabling GitOps on existing stacks
  3. Use the Git redeploy endpoint for manual updates of Git-based stacks

API Field Reference

The complete autoUpdate object structure:

{
  "autoUpdate": {
    "interval": "5m",           // Polling interval (e.g., "1m30s", "5m", "1h")
    "forcePullImage": true,     // Pull latest images on update
    "forceUpdate": false,       // Force redeployment even if no changes
    "webhook": "webhook-id",    // Webhook ID (optional, for webhook mechanism)
    "jobID": "15"              // Job ID (managed by Portainer)
  }
}

Original GitOps MCP Server Documentation

Features

  • GitOps Configuration: Enable/disable automatic updates for stacks
  • Update Mechanisms: Support for both webhook and polling-based updates
  • Edge Stack Support: GitOps for edge computing environments
  • Webhook Management: Generate and manage webhook URLs for CI/CD integration
  • Git Credentials: Manage authentication for private repositories
  • Manual Triggers: Trigger updates on-demand
  • Update Windows: Configure deployment windows (Business Edition)

Installation

  1. Ensure you have the Portainer MCP servers repository:

    git clone https://github.com/yourusername/portainer-mcp.git
    cd portainer-mcp
    
  2. Install dependencies:

    pip install mcp httpx aiohttp
    # Or use the requirements file:
    pip install -r requirements.txt
    
  3. Configure environment variables:

    cp .env.example .env
    # Edit .env with your Portainer URL and API key
    
  4. Make the server executable:

    chmod +x portainer_gitops_server.py
    

Configuration

Add to your Claude Desktop configuration:

{
  "portainer-gitops": {
    "command": "python",
    "args": ["/path/to/portainer-mcp/portainer_gitops_server.py"],
    "env": {
      "PORTAINER_URL": "https://your-portainer-instance.com",
      "PORTAINER_API_KEY": "your-api-key"
    }
  }
}

Available Tools

Stack GitOps Management

list_gitops_stacks

List all stacks with GitOps configurations.

  • Parameters:
    • environment_id (optional): Filter by environment ID

get_stack_gitops_config

Get GitOps configuration for a specific stack.

  • Parameters:
    • stack_id (required): Stack ID

enable_stack_gitops

Enable GitOps automatic updates for a stack.

  • Parameters:
    • stack_id (required): Stack ID
    • mechanism (optional): Update mechanism - "webhook" or "polling" (default: "polling")
    • interval (optional): Polling interval like "5m", "1h" (default: "5m")
    • force_update (optional): Force redeployment even if no changes (default: false)
    • pull_image (optional): Pull latest images on update (default: true)

disable_stack_gitops

Disable GitOps automatic updates for a stack.

  • Parameters:
    • stack_id (required): Stack ID

trigger_stack_update

Manually trigger a GitOps update for a stack.

  • Parameters:
    • stack_id (required): Stack ID
    • pull_image (optional): Pull latest images (default: true)

Webhook Management

get_stack_webhook

Get webhook URL for a stack.

  • Parameters:
    • stack_id (required): Stack ID

regenerate_stack_webhook

Regenerate webhook URL for a stack.

  • Parameters:
    • stack_id (required): Stack ID

Edge Stack GitOps

list_gitops_edge_stacks

List all edge stacks with GitOps configurations.

enable_edge_stack_gitops

Enable GitOps for an edge stack.

  • Parameters:
    • edge_stack_id (required): Edge Stack ID
    • mechanism (optional): Update mechanism (default: "polling")
    • interval (optional): Polling interval (default: "5m")
    • force_update (optional): Force redeployment (default: false)

get_edge_stack_webhook

Get webhook URL for an edge stack.

  • Parameters:
    • edge_stack_id (required): Edge Stack ID

Git Credential Management

create_git_credential

Create Git credentials for private repositories.

  • Parameters:
    • name (required): Credential name
    • username (required): Git username
    • password (required): Git password or personal access token

list_git_credentials

List all Git credentials.

delete_git_credential

Delete a Git credential.

  • Parameters:
    • credential_id (required): Credential ID

GitOps Settings

get_gitops_settings

Get global GitOps settings.

update_gitops_settings

Update global GitOps settings.

  • Parameters:
    • default_interval (optional): Default polling interval
    • concurrent_updates (optional): Max concurrent updates
    • update_window (optional): Update window configuration
      • enabled: Enable update window
      • start_time: Window start time
      • end_time: Window end time
      • timezone: Timezone for window

validate_git_repository

Validate access to a Git repository.

  • Parameters:
    • repository_url (required): Git repository URL
    • reference (optional): Branch or tag (default: "main")
    • credential_id (optional): Credential ID for private repos

Usage Examples

Enable GitOps with Polling

// Enable polling-based GitOps
await use_mcp_tool("portainer-gitops", "enable_stack_gitops", {
  stack_id: "5",
  mechanism: "polling",
  interval: "10m",
  force_update: false,
  pull_image: true
});

// Check configuration
await use_mcp_tool("portainer-gitops", "get_stack_gitops_config", {
  stack_id: "5"
});

Enable GitOps with Webhooks

// Enable webhook-based GitOps
await use_mcp_tool("portainer-gitops", "enable_stack_gitops", {
  stack_id: "7",
  mechanism: "webhook",
  pull_image: true
});

// Get webhook URL
await use_mcp_tool("portainer-gitops", "get_stack_webhook", {
  stack_id: "7"
});

Manage Git Credentials

// Create credential for private repository
await use_mcp_tool("portainer-gitops", "create_git_credential", {
  name: "GitHub PAT",
  username: "myusername",
  password: "ghp_xxxxxxxxxxxx"
});

// List all credentials
await use_mcp_tool("portainer-gitops", "list_git_credentials", {});

Edge Stack GitOps

// Enable GitOps for edge stack
await use_mcp_tool("portainer-gitops", "enable_edge_stack_gitops", {
  edge_stack_id: "3",
  mechanism: "polling",
  interval: "15m"
});

// List all GitOps edge stacks
await use_mcp_tool("portainer-gitops", "list_gitops_edge_stacks", {});

Manual Updates

// Trigger immediate update
await use_mcp_tool("portainer-gitops", "trigger_stack_update", {
  stack_id: "5",
  pull_image: true
});

GitOps Workflow

1. Setup Git Repository

  • Store your Docker Compose or Kubernetes manifests in Git
  • Use branches or tags for different environments
  • Configure CI/CD pipelines to update manifests

2. Deploy Stack from Git

  • Use the stacks server to create a stack from Git repository
  • Provide credentials if using a private repository

3. Enable GitOps

  • Choose between polling or webhook mechanism
  • Configure update intervals for polling
  • Set force update if you want Git as single source of truth

4. Webhook Integration

  • Add webhook URL to your Git repository
  • Configure to trigger on push events
  • Use in GitHub Actions or other CI/CD tools

5. Monitor Updates

  • Check GitOps status for stacks
  • Review update logs in Portainer
  • Use manual triggers for testing

Update Mechanisms

Polling

  • Portainer periodically checks Git repository for changes
  • Default interval: 5 minutes
  • Suitable for: Regular updates, less critical deployments
  • Pros: Simple setup, no external configuration
  • Cons: Delayed updates, resource usage

Webhooks

  • Git repository notifies Portainer of changes
  • Immediate updates on push
  • Suitable for: CI/CD pipelines, immediate deployments
  • Pros: Instant updates, event-driven
  • Cons: Requires webhook configuration in Git

Best Practices

  1. Use Webhooks for Production: Faster response to changes
  2. Set Appropriate Intervals: Balance between responsiveness and resource usage
  3. Enable Force Update Carefully: Can overwrite local changes
  4. Secure Credentials: Use personal access tokens, not passwords
  5. Test First: Use manual triggers to test deployments
  6. Monitor Logs: Check Portainer logs for update status
  7. Version Control: Use Git tags for production deployments

Security Considerations

  • Git credentials are stored encrypted in Portainer
  • Use personal access tokens with minimal permissions
  • Webhook URLs contain unique IDs for security
  • Enable HTTPS for webhook endpoints
  • Regularly rotate Git credentials
  • Use read-only tokens when possible

Troubleshooting

Common Issues

  1. Updates not triggering: Check Git credentials and repository access
  2. Webhook failures: Verify webhook URL and network connectivity
  3. Authentication errors: Ensure credentials have repository access
  4. Polling delays: Check interval settings and Portainer logs

Debug Mode

Enable debug logging by setting in your environment:

DEBUG=true
LOG_LEVEL=DEBUG

Requirements

  • Python 3.8+
  • Portainer Business Edition 2.19+ (for full GitOps features)
  • Valid Portainer API token
  • Git repository with Docker Compose or Kubernetes manifests
  • Network access between Portainer and Git repository