generated from personal-projects/leo-claude-mktplace
This commit provides comprehensive deployment documentation for production use. README.md updates: - Completely rewritten to reflect HTTP wrapper architecture - Clear distinction from standalone MCP server - Architecture diagram showing HTTP → wrapper → MCP → Gitea flow - Quick start guide with Docker - Configuration reference (required and optional) - HTTP endpoints documentation - Claude Desktop integration instructions - Troubleshooting section for common issues - Security considerations - References to DEPLOYMENT.md for advanced scenarios DEPLOYMENT.md (new): - Complete production deployment guide - Docker deployment (quick start and production config) - Security best practices: - Authentication setup - HTTPS configuration - Secrets management - Network isolation - Token rotation - Monitoring and health checks - Reverse proxy configurations (Nginx, Caddy, Traefik) - Cloud deployment guides: - AWS EC2 and ECS - Google Cloud Run - Azure Container Instances - Kubernetes deployment with full manifests - Troubleshooting production issues - Scaling considerations (horizontal, load balancing, caching) - Backup and disaster recovery - Production deployment checklist This documentation enables users to: 1. Get started quickly with Docker 2. Understand the architecture 3. Deploy securely in production 4. Scale and monitor the service 5. Troubleshoot common issues The documentation is deployment-focused and production-ready, covering real-world scenarios from local testing to enterprise cloud deployment. Closes #16 Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
713 lines
15 KiB
Markdown
713 lines
15 KiB
Markdown
# Deployment Guide
|
|
|
|
This guide covers production deployment of the Gitea HTTP MCP Wrapper in various environments.
|
|
|
|
## Table of Contents
|
|
|
|
1. [Prerequisites](#prerequisites)
|
|
2. [Docker Deployment](#docker-deployment)
|
|
3. [Security Best Practices](#security-best-practices)
|
|
4. [Monitoring and Health Checks](#monitoring-and-health-checks)
|
|
5. [Reverse Proxy Configuration](#reverse-proxy-configuration)
|
|
6. [Cloud Deployment](#cloud-deployment)
|
|
7. [Kubernetes Deployment](#kubernetes-deployment)
|
|
8. [Troubleshooting](#troubleshooting)
|
|
|
|
## Prerequisites
|
|
|
|
### Required
|
|
|
|
- Docker and Docker Compose (for Docker deployment)
|
|
- Gitea instance with API access
|
|
- Gitea API token with appropriate permissions
|
|
- Network connectivity between wrapper and Gitea instance
|
|
|
|
### Recommended
|
|
|
|
- HTTPS-capable reverse proxy (Nginx, Caddy, Traefik)
|
|
- Secrets management solution (not `.env` files in production)
|
|
- Monitoring and logging infrastructure
|
|
- Firewall or VPN for network security
|
|
|
|
## Docker Deployment
|
|
|
|
### Quick Start
|
|
|
|
1. **Clone the repository:**
|
|
|
|
```bash
|
|
git clone https://github.com/lmiranda/gitea-mcp-remote.git
|
|
cd gitea-mcp-remote
|
|
```
|
|
|
|
2. **Create configuration:**
|
|
|
|
```bash
|
|
cp .env.docker.example .env
|
|
nano .env # Edit with your values
|
|
```
|
|
|
|
Required configuration:
|
|
```bash
|
|
GITEA_URL=https://gitea.example.com
|
|
GITEA_TOKEN=your_gitea_api_token
|
|
GITEA_OWNER=your_username_or_org
|
|
GITEA_REPO=your_default_repo
|
|
AUTH_TOKEN=your_bearer_token # Recommended
|
|
```
|
|
|
|
3. **Start the service:**
|
|
|
|
```bash
|
|
docker-compose up -d
|
|
```
|
|
|
|
4. **Verify deployment:**
|
|
|
|
```bash
|
|
curl http://localhost:8000/health
|
|
```
|
|
|
|
### Production Configuration
|
|
|
|
For production, use a more robust `docker-compose.yml`:
|
|
|
|
```yaml
|
|
version: '3.8'
|
|
|
|
services:
|
|
gitea-mcp-wrapper:
|
|
build:
|
|
context: .
|
|
dockerfile: Dockerfile
|
|
image: gitea-mcp-wrapper:latest
|
|
container_name: gitea-mcp-wrapper
|
|
restart: always
|
|
ports:
|
|
- "127.0.0.1:8000:8000" # Bind to localhost only
|
|
environment:
|
|
- GITEA_URL=${GITEA_URL}
|
|
- GITEA_TOKEN=${GITEA_TOKEN}
|
|
- GITEA_OWNER=${GITEA_OWNER}
|
|
- GITEA_REPO=${GITEA_REPO}
|
|
- HTTP_HOST=0.0.0.0
|
|
- HTTP_PORT=8000
|
|
- AUTH_TOKEN=${AUTH_TOKEN}
|
|
healthcheck:
|
|
test: ["CMD", "python", "-c", "import urllib.request; urllib.request.urlopen('http://localhost:8000/health').read()"]
|
|
interval: 30s
|
|
timeout: 3s
|
|
retries: 3
|
|
start_period: 5s
|
|
logging:
|
|
driver: "json-file"
|
|
options:
|
|
max-size: "10m"
|
|
max-file: "3"
|
|
networks:
|
|
- gitea-mcp-network
|
|
|
|
networks:
|
|
gitea-mcp-network:
|
|
driver: bridge
|
|
```
|
|
|
|
### Docker Build Options
|
|
|
|
**Build the image:**
|
|
|
|
```bash
|
|
docker build -t gitea-mcp-wrapper:latest .
|
|
```
|
|
|
|
**Build with specific Python version:**
|
|
|
|
```bash
|
|
docker build --build-arg PYTHON_VERSION=3.11 -t gitea-mcp-wrapper:latest .
|
|
```
|
|
|
|
**Tag for registry:**
|
|
|
|
```bash
|
|
docker tag gitea-mcp-wrapper:latest registry.example.com/gitea-mcp-wrapper:latest
|
|
docker push registry.example.com/gitea-mcp-wrapper:latest
|
|
```
|
|
|
|
## Security Best Practices
|
|
|
|
### 1. Use Authentication
|
|
|
|
Always set `AUTH_TOKEN` in production:
|
|
|
|
```bash
|
|
# Generate a secure token
|
|
openssl rand -base64 32
|
|
|
|
# Add to .env
|
|
AUTH_TOKEN=<generated_token>
|
|
```
|
|
|
|
### 2. Use HTTPS
|
|
|
|
Never expose the wrapper directly to the internet without HTTPS. Use a reverse proxy (see below).
|
|
|
|
### 3. Network Isolation
|
|
|
|
- Bind to localhost only (`127.0.0.1`) if using a reverse proxy
|
|
- Use Docker networks to isolate services
|
|
- Consider VPN or private networking for access
|
|
|
|
### 4. Secrets Management
|
|
|
|
Don't use `.env` files in production. Use Docker secrets, Kubernetes secrets, or a secrets manager:
|
|
|
|
**Docker Secrets Example:**
|
|
|
|
```yaml
|
|
services:
|
|
gitea-mcp-wrapper:
|
|
secrets:
|
|
- gitea_token
|
|
- auth_token
|
|
environment:
|
|
- GITEA_TOKEN_FILE=/run/secrets/gitea_token
|
|
- AUTH_TOKEN_FILE=/run/secrets/auth_token
|
|
|
|
secrets:
|
|
gitea_token:
|
|
external: true
|
|
auth_token:
|
|
external: true
|
|
```
|
|
|
|
### 5. Regular Updates
|
|
|
|
- Rotate Gitea API token regularly
|
|
- Rotate AUTH_TOKEN regularly
|
|
- Keep Docker base image updated
|
|
- Update dependencies: `pip install --upgrade -r requirements.txt`
|
|
|
|
### 6. Minimal Permissions
|
|
|
|
Grant the Gitea API token only the minimum required permissions:
|
|
|
|
- Repository read/write
|
|
- Issue management
|
|
- Label management
|
|
- Milestone management
|
|
|
|
Avoid granting admin or organization-level permissions.
|
|
|
|
## Monitoring and Health Checks
|
|
|
|
### Health Check Endpoints
|
|
|
|
The wrapper provides three health check endpoints:
|
|
|
|
```bash
|
|
GET /health
|
|
GET /healthz
|
|
GET /ping
|
|
```
|
|
|
|
All return `{"status": "healthy"}` with HTTP 200 when the server is operational.
|
|
|
|
### Docker Health Checks
|
|
|
|
Docker automatically monitors the health check and can restart if unhealthy:
|
|
|
|
```yaml
|
|
healthcheck:
|
|
test: ["CMD", "curl", "-f", "http://localhost:8000/health"]
|
|
interval: 30s
|
|
timeout: 3s
|
|
retries: 3
|
|
start_period: 5s
|
|
```
|
|
|
|
### Monitoring Integration
|
|
|
|
**Prometheus metrics:** (Not yet implemented, but can be added)
|
|
|
|
**Log monitoring:**
|
|
|
|
```bash
|
|
# View logs
|
|
docker-compose logs -f gitea-mcp-wrapper
|
|
|
|
# JSON structured logs
|
|
docker logs gitea-mcp-wrapper --tail 100
|
|
```
|
|
|
|
**Uptime monitoring:**
|
|
|
|
Use tools like UptimeRobot, Pingdom, or Datadog to monitor `/health` endpoint.
|
|
|
|
## Reverse Proxy Configuration
|
|
|
|
### Nginx
|
|
|
|
```nginx
|
|
server {
|
|
listen 443 ssl http2;
|
|
server_name mcp.example.com;
|
|
|
|
ssl_certificate /etc/nginx/ssl/cert.pem;
|
|
ssl_certificate_key /etc/nginx/ssl/key.pem;
|
|
|
|
location / {
|
|
proxy_pass http://127.0.0.1:8000;
|
|
proxy_set_header Host $host;
|
|
proxy_set_header X-Real-IP $remote_addr;
|
|
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
|
proxy_set_header X-Forwarded-Proto $scheme;
|
|
|
|
# Pass through Authorization header
|
|
proxy_set_header Authorization $http_authorization;
|
|
proxy_pass_header Authorization;
|
|
|
|
# WebSocket support (if needed in future)
|
|
proxy_http_version 1.1;
|
|
proxy_set_header Upgrade $http_upgrade;
|
|
proxy_set_header Connection "upgrade";
|
|
|
|
# Timeouts
|
|
proxy_connect_timeout 60s;
|
|
proxy_send_timeout 60s;
|
|
proxy_read_timeout 60s;
|
|
}
|
|
|
|
# Health check endpoint (optional, can bypass auth)
|
|
location /health {
|
|
proxy_pass http://127.0.0.1:8000/health;
|
|
access_log off;
|
|
}
|
|
}
|
|
```
|
|
|
|
### Caddy
|
|
|
|
```caddyfile
|
|
mcp.example.com {
|
|
reverse_proxy localhost:8000 {
|
|
# Pass through Authorization header
|
|
header_up Authorization {>Authorization}
|
|
}
|
|
|
|
# Optional: Rate limiting
|
|
rate_limit {
|
|
zone mcp_zone
|
|
rate 100r/m
|
|
}
|
|
}
|
|
```
|
|
|
|
### Traefik
|
|
|
|
```yaml
|
|
# docker-compose.yml
|
|
services:
|
|
gitea-mcp-wrapper:
|
|
labels:
|
|
- "traefik.enable=true"
|
|
- "traefik.http.routers.mcp.rule=Host(`mcp.example.com`)"
|
|
- "traefik.http.routers.mcp.entrypoints=websecure"
|
|
- "traefik.http.routers.mcp.tls.certresolver=letsencrypt"
|
|
- "traefik.http.services.mcp.loadbalancer.server.port=8000"
|
|
```
|
|
|
|
## Cloud Deployment
|
|
|
|
### AWS EC2
|
|
|
|
1. **Launch EC2 instance:**
|
|
- Amazon Linux 2 or Ubuntu 22.04
|
|
- t3.micro or larger
|
|
- Security group: Allow port 443 (HTTPS)
|
|
|
|
2. **Install Docker:**
|
|
|
|
```bash
|
|
sudo yum update -y
|
|
sudo yum install -y docker
|
|
sudo service docker start
|
|
sudo usermod -aG docker ec2-user
|
|
```
|
|
|
|
3. **Deploy wrapper:**
|
|
|
|
```bash
|
|
git clone https://github.com/lmiranda/gitea-mcp-remote.git
|
|
cd gitea-mcp-remote
|
|
cp .env.docker.example .env
|
|
nano .env # Configure
|
|
docker-compose up -d
|
|
```
|
|
|
|
4. **Configure Nginx or ALB for HTTPS**
|
|
|
|
### AWS ECS (Fargate)
|
|
|
|
1. **Create task definition:**
|
|
|
|
```json
|
|
{
|
|
"family": "gitea-mcp-wrapper",
|
|
"networkMode": "awsvpc",
|
|
"requiresCompatibilities": ["FARGATE"],
|
|
"cpu": "256",
|
|
"memory": "512",
|
|
"containerDefinitions": [
|
|
{
|
|
"name": "gitea-mcp-wrapper",
|
|
"image": "your-ecr-repo/gitea-mcp-wrapper:latest",
|
|
"portMappings": [
|
|
{
|
|
"containerPort": 8000,
|
|
"protocol": "tcp"
|
|
}
|
|
],
|
|
"environment": [
|
|
{"name": "GITEA_URL", "value": "https://gitea.example.com"},
|
|
{"name": "HTTP_HOST", "value": "0.0.0.0"},
|
|
{"name": "HTTP_PORT", "value": "8000"}
|
|
],
|
|
"secrets": [
|
|
{
|
|
"name": "GITEA_TOKEN",
|
|
"valueFrom": "arn:aws:secretsmanager:region:account:secret:gitea-token"
|
|
},
|
|
{
|
|
"name": "AUTH_TOKEN",
|
|
"valueFrom": "arn:aws:secretsmanager:region:account:secret:auth-token"
|
|
}
|
|
],
|
|
"healthCheck": {
|
|
"command": ["CMD-SHELL", "curl -f http://localhost:8000/health || exit 1"],
|
|
"interval": 30,
|
|
"timeout": 5,
|
|
"retries": 3,
|
|
"startPeriod": 10
|
|
}
|
|
}
|
|
]
|
|
}
|
|
```
|
|
|
|
2. **Create ECS service with ALB**
|
|
|
|
### Google Cloud Run
|
|
|
|
1. **Build and push image:**
|
|
|
|
```bash
|
|
gcloud builds submit --tag gcr.io/PROJECT_ID/gitea-mcp-wrapper
|
|
```
|
|
|
|
2. **Deploy:**
|
|
|
|
```bash
|
|
gcloud run deploy gitea-mcp-wrapper \
|
|
--image gcr.io/PROJECT_ID/gitea-mcp-wrapper \
|
|
--platform managed \
|
|
--region us-central1 \
|
|
--allow-unauthenticated \
|
|
--set-env-vars GITEA_URL=https://gitea.example.com \
|
|
--set-secrets GITEA_TOKEN=gitea-token:latest,AUTH_TOKEN=auth-token:latest \
|
|
--port 8000
|
|
```
|
|
|
|
### Azure Container Instances
|
|
|
|
```bash
|
|
az container create \
|
|
--resource-group myResourceGroup \
|
|
--name gitea-mcp-wrapper \
|
|
--image your-registry/gitea-mcp-wrapper:latest \
|
|
--ports 8000 \
|
|
--dns-name-label gitea-mcp \
|
|
--environment-variables \
|
|
GITEA_URL=https://gitea.example.com \
|
|
HTTP_HOST=0.0.0.0 \
|
|
HTTP_PORT=8000 \
|
|
--secure-environment-variables \
|
|
GITEA_TOKEN=your_token \
|
|
AUTH_TOKEN=your_auth_token
|
|
```
|
|
|
|
## Kubernetes Deployment
|
|
|
|
### Deployment Manifest
|
|
|
|
```yaml
|
|
apiVersion: apps/v1
|
|
kind: Deployment
|
|
metadata:
|
|
name: gitea-mcp-wrapper
|
|
namespace: default
|
|
spec:
|
|
replicas: 2
|
|
selector:
|
|
matchLabels:
|
|
app: gitea-mcp-wrapper
|
|
template:
|
|
metadata:
|
|
labels:
|
|
app: gitea-mcp-wrapper
|
|
spec:
|
|
containers:
|
|
- name: gitea-mcp-wrapper
|
|
image: your-registry/gitea-mcp-wrapper:latest
|
|
ports:
|
|
- containerPort: 8000
|
|
env:
|
|
- name: GITEA_URL
|
|
value: "https://gitea.example.com"
|
|
- name: HTTP_HOST
|
|
value: "0.0.0.0"
|
|
- name: HTTP_PORT
|
|
value: "8000"
|
|
- name: GITEA_TOKEN
|
|
valueFrom:
|
|
secretKeyRef:
|
|
name: gitea-secrets
|
|
key: token
|
|
- name: AUTH_TOKEN
|
|
valueFrom:
|
|
secretKeyRef:
|
|
name: gitea-secrets
|
|
key: auth-token
|
|
livenessProbe:
|
|
httpGet:
|
|
path: /health
|
|
port: 8000
|
|
initialDelaySeconds: 10
|
|
periodSeconds: 30
|
|
readinessProbe:
|
|
httpGet:
|
|
path: /health
|
|
port: 8000
|
|
initialDelaySeconds: 5
|
|
periodSeconds: 10
|
|
resources:
|
|
requests:
|
|
memory: "128Mi"
|
|
cpu: "100m"
|
|
limits:
|
|
memory: "256Mi"
|
|
cpu: "200m"
|
|
---
|
|
apiVersion: v1
|
|
kind: Service
|
|
metadata:
|
|
name: gitea-mcp-wrapper
|
|
namespace: default
|
|
spec:
|
|
selector:
|
|
app: gitea-mcp-wrapper
|
|
ports:
|
|
- protocol: TCP
|
|
port: 80
|
|
targetPort: 8000
|
|
type: ClusterIP
|
|
---
|
|
apiVersion: networking.k8s.io/v1
|
|
kind: Ingress
|
|
metadata:
|
|
name: gitea-mcp-wrapper
|
|
namespace: default
|
|
annotations:
|
|
cert-manager.io/cluster-issuer: "letsencrypt-prod"
|
|
spec:
|
|
tls:
|
|
- hosts:
|
|
- mcp.example.com
|
|
secretName: mcp-tls
|
|
rules:
|
|
- host: mcp.example.com
|
|
http:
|
|
paths:
|
|
- path: /
|
|
pathType: Prefix
|
|
backend:
|
|
service:
|
|
name: gitea-mcp-wrapper
|
|
port:
|
|
number: 80
|
|
```
|
|
|
|
### Secrets Management
|
|
|
|
```bash
|
|
# Create secret
|
|
kubectl create secret generic gitea-secrets \
|
|
--from-literal=token=your_gitea_token \
|
|
--from-literal=auth-token=your_auth_token \
|
|
--namespace=default
|
|
```
|
|
|
|
## Troubleshooting
|
|
|
|
### Container Won't Start
|
|
|
|
```bash
|
|
# Check logs
|
|
docker-compose logs gitea-mcp-wrapper
|
|
|
|
# Check container status
|
|
docker-compose ps
|
|
|
|
# Rebuild image
|
|
docker-compose build --no-cache
|
|
docker-compose up -d
|
|
```
|
|
|
|
### Health Check Failing
|
|
|
|
```bash
|
|
# Test health endpoint directly
|
|
docker exec gitea-mcp-wrapper curl http://localhost:8000/health
|
|
|
|
# Check if server is listening
|
|
docker exec gitea-mcp-wrapper netstat -tlnp
|
|
```
|
|
|
|
### Cannot Reach Gitea from Container
|
|
|
|
```bash
|
|
# Test connectivity
|
|
docker exec gitea-mcp-wrapper curl -v https://gitea.example.com
|
|
|
|
# Check DNS resolution
|
|
docker exec gitea-mcp-wrapper nslookup gitea.example.com
|
|
|
|
# For docker-compose, ensure network allows egress
|
|
```
|
|
|
|
### High Memory Usage
|
|
|
|
```bash
|
|
# Check container stats
|
|
docker stats gitea-mcp-wrapper
|
|
|
|
# Adjust resource limits in docker-compose.yml
|
|
deploy:
|
|
resources:
|
|
limits:
|
|
memory: 256M
|
|
```
|
|
|
|
### Authentication Failures
|
|
|
|
```bash
|
|
# Verify AUTH_TOKEN is set
|
|
docker exec gitea-mcp-wrapper printenv AUTH_TOKEN
|
|
|
|
# Test with curl
|
|
curl -H "Authorization: Bearer YOUR_TOKEN" http://localhost:8000/tools/list
|
|
|
|
# Check logs for auth failures
|
|
docker-compose logs gitea-mcp-wrapper | grep -i auth
|
|
```
|
|
|
|
## Scaling Considerations
|
|
|
|
### Horizontal Scaling
|
|
|
|
The wrapper is stateless and can be scaled horizontally:
|
|
|
|
```yaml
|
|
# docker-compose.yml
|
|
services:
|
|
gitea-mcp-wrapper:
|
|
deploy:
|
|
replicas: 3
|
|
```
|
|
|
|
Or in Kubernetes:
|
|
|
|
```bash
|
|
kubectl scale deployment gitea-mcp-wrapper --replicas=5
|
|
```
|
|
|
|
### Load Balancing
|
|
|
|
Use a load balancer to distribute traffic:
|
|
- Docker Swarm: Built-in load balancing
|
|
- Kubernetes: Service with multiple pods
|
|
- Cloud: AWS ALB, GCP Load Balancer, Azure Load Balancer
|
|
|
|
### Caching
|
|
|
|
Consider caching responses to reduce Gitea API load:
|
|
- Add Redis or Memcached
|
|
- Cache tool list responses
|
|
- Cache frequently accessed issues/labels
|
|
|
|
### Rate Limiting
|
|
|
|
Implement rate limiting at reverse proxy level to prevent API abuse:
|
|
|
|
**Nginx:**
|
|
```nginx
|
|
limit_req_zone $binary_remote_addr zone=mcp:10m rate=10r/s;
|
|
limit_req zone=mcp burst=20 nodelay;
|
|
```
|
|
|
|
**Caddy:**
|
|
```caddyfile
|
|
rate_limit {
|
|
rate 100r/m
|
|
}
|
|
```
|
|
|
|
## Backup and Disaster Recovery
|
|
|
|
### Configuration Backup
|
|
|
|
```bash
|
|
# Backup .env file
|
|
cp .env .env.backup.$(date +%Y%m%d)
|
|
|
|
# Backup docker-compose.yml
|
|
cp docker-compose.yml docker-compose.yml.backup.$(date +%Y%m%d)
|
|
```
|
|
|
|
### Image Backup
|
|
|
|
```bash
|
|
# Save Docker image
|
|
docker save gitea-mcp-wrapper:latest | gzip > gitea-mcp-wrapper-backup.tar.gz
|
|
|
|
# Load Docker image
|
|
docker load < gitea-mcp-wrapper-backup.tar.gz
|
|
```
|
|
|
|
### Recovery Plan
|
|
|
|
1. Restore configuration files
|
|
2. Rebuild or load Docker image
|
|
3. Start services: `docker-compose up -d`
|
|
4. Verify health: `curl http://localhost:8000/health`
|
|
5. Test authentication and tool access
|
|
|
|
## Production Checklist
|
|
|
|
- [ ] HTTPS configured via reverse proxy
|
|
- [ ] `AUTH_TOKEN` set and secure
|
|
- [ ] Secrets stored in secrets manager (not `.env`)
|
|
- [ ] Health checks configured
|
|
- [ ] Monitoring and alerting set up
|
|
- [ ] Logs aggregated and retained
|
|
- [ ] Firewall rules configured
|
|
- [ ] Rate limiting enabled
|
|
- [ ] Resource limits set
|
|
- [ ] Backup strategy in place
|
|
- [ ] Disaster recovery plan documented
|
|
- [ ] Security updates scheduled
|
|
- [ ] Token rotation process defined
|
|
|
|
---
|
|
|
|
**For questions or issues, please open an issue on the [GitHub repository](https://github.com/lmiranda/gitea-mcp-remote/issues).**
|