feat: streamline Docker deployment with registry images and unified env configuration

- Update docker-compose.yml to use pre-built images from GitLab registry
- Replace individual environment variables with unified env_file directive
- Create comprehensive .env.example with detailed instructions and troubleshooting
- Add push-to-registry.sh script for building and pushing images to registry
- Add docker-compose.prod.yml as reference for production deployments
- Update documentation to reflect simplified deployment process

Users can now deploy with just:
  cp .env.example .env
  docker-compose pull
  docker-compose up -d

All 7 MCP server images are available at:
  git.oe74.net/adelorenzo/portainer-mcp/portainer-*:latest

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

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
Adolfo Delorenzo 2025-07-21 15:16:15 -03:00
parent 2098453ff1
commit 10dfd606c0
6 changed files with 340 additions and 87 deletions

View File

@ -1,18 +1,97 @@
# Portainer MCP Environment Configuration # Portainer MCP Server Suite Environment Configuration
# Copy this file to .env and update with your values # =====================================================
# This file contains all environment variables used by the Portainer MCP servers.
# Copy this file to .env and update with your actual values.
#
# IMPORTANT: Never commit the .env file to version control as it contains sensitive credentials.
# Required - Your Portainer instance URL # REQUIRED CONFIGURATION
# ----------------------
# Your Portainer instance URL (must be accessible from Docker containers)
# Example: https://portainer.example.com
# Example: http://192.168.1.100:9000
PORTAINER_URL=https://your-portainer-instance.com PORTAINER_URL=https://your-portainer-instance.com
# Required - Your Portainer API key # Your Portainer API key (also called Access Token)
# Generate from Portainer UI: My Account > Access Tokens # To generate an API key:
PORTAINER_API_KEY=your-api-key-here # 1. Log in to your Portainer instance
# 2. Click on your username in the top right
# 3. Go to "My account" → "Access tokens"
# 4. Click "Add access token"
# 5. Give it a name (e.g., "MCP Servers")
# 6. Copy the generated token and paste it here
PORTAINER_API_KEY=ptr_your-api-key-here
# Optional - Set to true if using self-signed certificates # OPTIONAL CONFIGURATION
# ----------------------
# Set to true if using self-signed SSL certificates
# This disables SSL certificate verification (use with caution)
# Default: false
PORTAINER_INSECURE=false PORTAINER_INSECURE=false
# Optional - HTTP request timeout in seconds # HTTP request timeout in seconds
# Increase this value if you have slow network connections or large operations
# Default: 30
HTTP_TIMEOUT=30 HTTP_TIMEOUT=30
# Optional - Maximum number of retry attempts # Maximum number of retry attempts for failed API calls
MAX_RETRIES=3 # The servers will retry with exponential backoff
# Default: 3
MAX_RETRIES=3
# ADVANCED CONFIGURATION (rarely needed)
# --------------------------------------
# Log level for debugging (DEBUG, INFO, WARNING, ERROR)
# Default: INFO
# LOG_LEVEL=INFO
# Enable debug mode for verbose output
# Default: false
# DEBUG=false
# DOCKER COMPOSE SPECIFIC
# -----------------------
# These variables are used by docker-compose.yml but not by the MCP servers
# Docker registry to pull images from (if using private registry)
# Default uses the GitLab registry where images are hosted
# DOCKER_REGISTRY=git.oe74.net/adelorenzo/portainer-mcp
# Image tag to use for all services
# Default: latest
# IMAGE_TAG=latest
# NETWORK CONFIGURATION
# ---------------------
# If your Portainer instance is running in Docker on the same host,
# you might need to use the Docker bridge network IP or container name
# Example: http://portainer:9000 (if Portainer container is named 'portainer')
# Example: http://172.17.0.1:9000 (Docker bridge IP on Linux)
# TROUBLESHOOTING TIPS
# --------------------
#
# 1. Connection refused errors:
# - Ensure PORTAINER_URL is accessible from within Docker containers
# - Try using the Docker host IP instead of localhost
# - Check if Portainer is running and accessible
#
# 2. Authentication errors:
# - Verify your API key is valid and not expired
# - Ensure the API key has the necessary permissions
# - Check if your Portainer user has the required role (Administrator/Operator)
#
# 3. SSL certificate errors:
# - Set PORTAINER_INSECURE=true for self-signed certificates
# - Ensure your Portainer instance has valid SSL certificates
#
# 4. Timeout errors:
# - Increase HTTP_TIMEOUT for slow connections
# - Check network connectivity between containers and Portainer
#
# 5. To test your configuration:
# docker-compose run --rm portainer-core python -c "import os; print('URL:', os.getenv('PORTAINER_URL'))"

View File

@ -21,10 +21,15 @@ This guide covers how to build and run the Portainer MCP servers using Docker co
```bash ```bash
cp .env.example .env cp .env.example .env
# Edit .env with your Portainer credentials # Edit .env with your Portainer credentials
nano .env # or use your preferred editor
``` ```
3. **Build all containers** 3. **Pull images (or build locally)**
```bash ```bash
# Option A: Pull from registry (recommended)
docker-compose pull
# Option B: Build locally
./build-docker.sh all ./build-docker.sh all
``` ```
@ -92,19 +97,26 @@ Each MCP server runs in its own container with the following configuration:
## Environment Variables ## Environment Variables
All containers share the same environment variables: All containers use a shared `.env` file for configuration. The docker-compose.yml file is configured to automatically load environment variables from this file using the `env_file` directive.
```bash ```yaml
# Required # docker-compose.yml snippet
PORTAINER_URL=https://your-portainer-instance.com services:
PORTAINER_API_KEY=your-api-key-here portainer-core:
image: git.oe74.net/adelorenzo/portainer-mcp/portainer-core:latest
# Optional env_file:
PORTAINER_INSECURE=false # Set to true for self-signed certificates - .env
HTTP_TIMEOUT=30
MAX_RETRIES=3
``` ```
See `.env.example` for a complete list of available environment variables with detailed documentation and troubleshooting tips.
### Key Variables:
- `PORTAINER_URL` - Your Portainer instance URL (required)
- `PORTAINER_API_KEY` - Your Portainer API token (required)
- `PORTAINER_INSECURE` - Set to true for self-signed certificates
- `HTTP_TIMEOUT` - Request timeout in seconds
- `MAX_RETRIES` - Number of retry attempts
## Docker Compose Configuration ## Docker Compose Configuration
The `docker-compose.yml` file defines all services with: The `docker-compose.yml` file defines all services with:

View File

@ -12,7 +12,7 @@ cd portainer-mcp
# Docker deployment (recommended) # Docker deployment (recommended)
cp .env.example .env cp .env.example .env
# Edit .env with your Portainer URL and API key # Edit .env with your Portainer URL and API key
./build-docker.sh all docker-compose pull
docker-compose up -d docker-compose up -d
# Or Python local installation # Or Python local installation
@ -122,12 +122,14 @@ git clone https://github.com/yourusername/portainer-mcp.git
cd portainer-mcp cd portainer-mcp
``` ```
2. Build and run with Docker Compose: 2. Configure environment and run:
```bash ```bash
cp .env.example .env cp .env.example .env
# Edit .env with your Portainer credentials # Edit .env with your Portainer credentials
nano .env # or use your preferred editor
./build-docker.sh all # Pull pre-built images from registry
docker-compose pull
docker-compose up -d docker-compose up -d
``` ```

111
docker-compose.prod.yml Normal file
View File

@ -0,0 +1,111 @@
version: '3.8'
services:
portainer-core:
image: git.oe74.net/adelorenzo/portainer-mcp/portainer-core:latest
container_name: portainer-mcp-core
ports:
- "3000:3000"
environment:
- PORTAINER_URL=${PORTAINER_URL}
- PORTAINER_API_KEY=${PORTAINER_API_KEY}
- PORTAINER_INSECURE=${PORTAINER_INSECURE:-false}
- HTTP_TIMEOUT=${HTTP_TIMEOUT:-30}
- MAX_RETRIES=${MAX_RETRIES:-3}
restart: unless-stopped
networks:
- portainer-mcp
portainer-environments:
image: git.oe74.net/adelorenzo/portainer-mcp/portainer-environments:latest
container_name: portainer-mcp-environments
ports:
- "3001:3001"
environment:
- PORTAINER_URL=${PORTAINER_URL}
- PORTAINER_API_KEY=${PORTAINER_API_KEY}
- PORTAINER_INSECURE=${PORTAINER_INSECURE:-false}
- HTTP_TIMEOUT=${HTTP_TIMEOUT:-30}
- MAX_RETRIES=${MAX_RETRIES:-3}
restart: unless-stopped
networks:
- portainer-mcp
portainer-docker:
image: git.oe74.net/adelorenzo/portainer-mcp/portainer-docker:latest
container_name: portainer-mcp-docker
ports:
- "3002:3002"
environment:
- PORTAINER_URL=${PORTAINER_URL}
- PORTAINER_API_KEY=${PORTAINER_API_KEY}
- PORTAINER_INSECURE=${PORTAINER_INSECURE:-false}
- HTTP_TIMEOUT=${HTTP_TIMEOUT:-30}
- MAX_RETRIES=${MAX_RETRIES:-3}
restart: unless-stopped
networks:
- portainer-mcp
portainer-kubernetes:
image: git.oe74.net/adelorenzo/portainer-mcp/portainer-kubernetes:latest
container_name: portainer-mcp-kubernetes
ports:
- "3003:3003"
environment:
- PORTAINER_URL=${PORTAINER_URL}
- PORTAINER_API_KEY=${PORTAINER_API_KEY}
- PORTAINER_INSECURE=${PORTAINER_INSECURE:-false}
- HTTP_TIMEOUT=${HTTP_TIMEOUT:-30}
- MAX_RETRIES=${MAX_RETRIES:-3}
restart: unless-stopped
networks:
- portainer-mcp
portainer-stacks:
image: git.oe74.net/adelorenzo/portainer-mcp/portainer-stacks:latest
container_name: portainer-mcp-stacks
ports:
- "3004:3004"
environment:
- PORTAINER_URL=${PORTAINER_URL}
- PORTAINER_API_KEY=${PORTAINER_API_KEY}
- PORTAINER_INSECURE=${PORTAINER_INSECURE:-false}
- HTTP_TIMEOUT=${HTTP_TIMEOUT:-30}
- MAX_RETRIES=${MAX_RETRIES:-3}
restart: unless-stopped
networks:
- portainer-mcp
portainer-edge:
image: git.oe74.net/adelorenzo/portainer-mcp/portainer-edge:latest
container_name: portainer-mcp-edge
ports:
- "3005:3005"
environment:
- PORTAINER_URL=${PORTAINER_URL}
- PORTAINER_API_KEY=${PORTAINER_API_KEY}
- PORTAINER_INSECURE=${PORTAINER_INSECURE:-false}
- HTTP_TIMEOUT=${HTTP_TIMEOUT:-30}
- MAX_RETRIES=${MAX_RETRIES:-3}
restart: unless-stopped
networks:
- portainer-mcp
portainer-gitops:
image: git.oe74.net/adelorenzo/portainer-mcp/portainer-gitops:latest
container_name: portainer-mcp-gitops
ports:
- "3006:3006"
environment:
- PORTAINER_URL=${PORTAINER_URL}
- PORTAINER_API_KEY=${PORTAINER_API_KEY}
- PORTAINER_INSECURE=${PORTAINER_INSECURE:-false}
- HTTP_TIMEOUT=${HTTP_TIMEOUT:-30}
- MAX_RETRIES=${MAX_RETRIES:-3}
restart: unless-stopped
networks:
- portainer-mcp
networks:
portainer-mcp:
driver: bridge

View File

@ -2,120 +2,99 @@ version: '3.8'
services: services:
portainer-core: portainer-core:
build: image: git.oe74.net/adelorenzo/portainer-mcp/portainer-core:latest
context: . # build:
dockerfile: docker/Dockerfile.core # context: .
# dockerfile: docker/Dockerfile.core
container_name: portainer-mcp-core container_name: portainer-mcp-core
ports: ports:
- "3000:3000" - "3000:3000"
environment: env_file:
- PORTAINER_URL=${PORTAINER_URL} - .env
- PORTAINER_API_KEY=${PORTAINER_API_KEY}
- PORTAINER_INSECURE=${PORTAINER_INSECURE:-false}
- HTTP_TIMEOUT=${HTTP_TIMEOUT:-30}
- MAX_RETRIES=${MAX_RETRIES:-3}
restart: unless-stopped restart: unless-stopped
networks: networks:
- portainer-mcp - portainer-mcp
portainer-environments: portainer-environments:
build: image: git.oe74.net/adelorenzo/portainer-mcp/portainer-environments:latest
context: . # build:
dockerfile: docker/Dockerfile.environments # context: .
# dockerfile: docker/Dockerfile.environments
container_name: portainer-mcp-environments container_name: portainer-mcp-environments
ports: ports:
- "3001:3001" - "3001:3001"
environment: env_file:
- PORTAINER_URL=${PORTAINER_URL} - .env
- PORTAINER_API_KEY=${PORTAINER_API_KEY}
- PORTAINER_INSECURE=${PORTAINER_INSECURE:-false}
- HTTP_TIMEOUT=${HTTP_TIMEOUT:-30}
- MAX_RETRIES=${MAX_RETRIES:-3}
restart: unless-stopped restart: unless-stopped
networks: networks:
- portainer-mcp - portainer-mcp
portainer-docker: portainer-docker:
build: image: git.oe74.net/adelorenzo/portainer-mcp/portainer-docker:latest
context: . # build:
dockerfile: docker/Dockerfile.docker # context: .
# dockerfile: docker/Dockerfile.docker
container_name: portainer-mcp-docker container_name: portainer-mcp-docker
ports: ports:
- "3002:3002" - "3002:3002"
environment: env_file:
- PORTAINER_URL=${PORTAINER_URL} - .env
- PORTAINER_API_KEY=${PORTAINER_API_KEY}
- PORTAINER_INSECURE=${PORTAINER_INSECURE:-false}
- HTTP_TIMEOUT=${HTTP_TIMEOUT:-30}
- MAX_RETRIES=${MAX_RETRIES:-3}
restart: unless-stopped restart: unless-stopped
networks: networks:
- portainer-mcp - portainer-mcp
portainer-kubernetes: portainer-kubernetes:
build: image: git.oe74.net/adelorenzo/portainer-mcp/portainer-kubernetes:latest
context: . # build:
dockerfile: docker/Dockerfile.kubernetes # context: .
# dockerfile: docker/Dockerfile.kubernetes
container_name: portainer-mcp-kubernetes container_name: portainer-mcp-kubernetes
ports: ports:
- "3003:3003" - "3003:3003"
environment: env_file:
- PORTAINER_URL=${PORTAINER_URL} - .env
- PORTAINER_API_KEY=${PORTAINER_API_KEY}
- PORTAINER_INSECURE=${PORTAINER_INSECURE:-false}
- HTTP_TIMEOUT=${HTTP_TIMEOUT:-30}
- MAX_RETRIES=${MAX_RETRIES:-3}
restart: unless-stopped restart: unless-stopped
networks: networks:
- portainer-mcp - portainer-mcp
portainer-stacks: portainer-stacks:
build: image: git.oe74.net/adelorenzo/portainer-mcp/portainer-stacks:latest
context: . # build:
dockerfile: docker/Dockerfile.stacks # context: .
# dockerfile: docker/Dockerfile.stacks
container_name: portainer-mcp-stacks container_name: portainer-mcp-stacks
ports: ports:
- "3004:3004" - "3004:3004"
environment: env_file:
- PORTAINER_URL=${PORTAINER_URL} - .env
- PORTAINER_API_KEY=${PORTAINER_API_KEY}
- PORTAINER_INSECURE=${PORTAINER_INSECURE:-false}
- HTTP_TIMEOUT=${HTTP_TIMEOUT:-30}
- MAX_RETRIES=${MAX_RETRIES:-3}
restart: unless-stopped restart: unless-stopped
networks: networks:
- portainer-mcp - portainer-mcp
portainer-edge: portainer-edge:
build: image: git.oe74.net/adelorenzo/portainer-mcp/portainer-edge:latest
context: . # build:
dockerfile: docker/Dockerfile.edge # context: .
# dockerfile: docker/Dockerfile.edge
container_name: portainer-mcp-edge container_name: portainer-mcp-edge
ports: ports:
- "3005:3005" - "3005:3005"
environment: env_file:
- PORTAINER_URL=${PORTAINER_URL} - .env
- PORTAINER_API_KEY=${PORTAINER_API_KEY}
- PORTAINER_INSECURE=${PORTAINER_INSECURE:-false}
- HTTP_TIMEOUT=${HTTP_TIMEOUT:-30}
- MAX_RETRIES=${MAX_RETRIES:-3}
restart: unless-stopped restart: unless-stopped
networks: networks:
- portainer-mcp - portainer-mcp
portainer-gitops: portainer-gitops:
build: image: git.oe74.net/adelorenzo/portainer-mcp/portainer-gitops:latest
context: . # build:
dockerfile: docker/Dockerfile.gitops # context: .
# dockerfile: docker/Dockerfile.gitops
container_name: portainer-mcp-gitops container_name: portainer-mcp-gitops
ports: ports:
- "3006:3006" - "3006:3006"
environment: env_file:
- PORTAINER_URL=${PORTAINER_URL} - .env
- PORTAINER_API_KEY=${PORTAINER_API_KEY}
- PORTAINER_INSECURE=${PORTAINER_INSECURE:-false}
- HTTP_TIMEOUT=${HTTP_TIMEOUT:-30}
- MAX_RETRIES=${MAX_RETRIES:-3}
restart: unless-stopped restart: unless-stopped
networks: networks:
- portainer-mcp - portainer-mcp

70
push-to-registry.sh Executable file
View File

@ -0,0 +1,70 @@
#!/bin/bash
# Build and push Portainer MCP Docker images to GitLab container registry
set -e
# Configuration
REGISTRY="git.oe74.net/adelorenzo/portainer-mcp"
VERSION="${1:-latest}"
# Colors for output
GREEN='\033[0;32m'
BLUE='\033[0;34m'
YELLOW='\033[1;33m'
RED='\033[0;31m'
NC='\033[0m' # No Color
echo -e "${BLUE}Building and pushing Portainer MCP Docker images to GitLab registry${NC}"
echo -e "${BLUE}Registry: ${REGISTRY}${NC}"
echo -e "${BLUE}Version: ${VERSION}${NC}"
echo ""
# Check if logged in to registry
echo -e "${YELLOW}Checking GitLab registry login...${NC}"
if ! docker login git.oe74.net 2>/dev/null; then
echo -e "${RED}Please login to GitLab registry first:${NC}"
echo "docker login git.oe74.net"
exit 1
fi
# Function to build and push a single image
build_and_push() {
local service=$1
local image_name="${REGISTRY}/portainer-${service}"
echo -e "${BLUE}Building portainer-${service}...${NC}"
docker build -f docker/Dockerfile.${service} -t ${image_name}:${VERSION} .
if [ "${VERSION}" != "latest" ]; then
docker tag ${image_name}:${VERSION} ${image_name}:latest
fi
echo -e "${BLUE}Pushing portainer-${service} to registry...${NC}"
docker push ${image_name}:${VERSION}
if [ "${VERSION}" != "latest" ]; then
docker push ${image_name}:latest
fi
echo -e "${GREEN}✓ portainer-${service} pushed successfully${NC}"
echo ""
}
# Build and push all services
services=("core" "environments" "docker" "kubernetes" "stacks" "edge" "gitops")
for service in "${services[@]}"; do
build_and_push "${service}"
done
echo -e "${GREEN}All images built and pushed successfully!${NC}"
echo ""
echo "Images available at:"
for service in "${services[@]}"; do
echo " ${REGISTRY}/portainer-${service}:${VERSION}"
done
echo ""
echo "To use these images, update your docker-compose.yml:"
echo " image: ${REGISTRY}/portainer-core:${VERSION}"
echo " # instead of build: ..."