From edb9c5109611203acb867aaf15e0b6d94079aa30 Mon Sep 17 00:00:00 2001 From: czlonkowski <56956555+czlonkowski@users.noreply.github.com> Date: Fri, 13 Jun 2025 15:07:02 +0200 Subject: [PATCH] docs: add Docker deployment plan with phased implementation strategy --- docs/DOCKER_DEPLOYMENT_PLAN.md | 1073 ++++++++++++++++++++++++++++++++ 1 file changed, 1073 insertions(+) create mode 100644 docs/DOCKER_DEPLOYMENT_PLAN.md diff --git a/docs/DOCKER_DEPLOYMENT_PLAN.md b/docs/DOCKER_DEPLOYMENT_PLAN.md new file mode 100644 index 0000000..d70e6a4 --- /dev/null +++ b/docs/DOCKER_DEPLOYMENT_PLAN.md @@ -0,0 +1,1073 @@ +# Docker Deployment Plan for n8n-MCP (v2) + +## Executive Summary + +This document outlines a phased plan to enhance the developer experience of n8n-MCP through Docker deployment. We'll start with a simple, working solution and progressively add features like nginx HTTPS support based on user needs. + +## Goals + +1. **One-Command Deployment**: Users should be able to run `docker compose up -d` after minimal configuration +2. **Progressive Enhancement**: Start simple, add complexity only when needed +3. **Dual Mode Support**: Single Docker image supporting both stdio (local) and HTTP (remote) modes +4. **Automated Builds**: GitHub Actions workflow for building and publishing to GitHub Container Registry (ghcr.io) +5. **Database Persistence**: Reliable SQLite database persistence with proper volume management +6. **Cross-Platform Support**: Multi-architecture images (amd64, arm64) for broad compatibility + +## Implementation Strategy + +### Phase 1: Simple Docker (Week 1) +- Basic Dockerfile without nginx +- Simple docker-compose.yml +- Essential features only +- Focus on ease of use + +### Phase 2: Enhanced Security (Week 2) +- Optional nginx support +- HTTPS capabilities +- Advanced configurations +- Production-ready features + +### Phase 3: CI/CD & Documentation (Week 3) +- GitHub Actions setup +- Comprehensive documentation +- Community feedback integration + +## Architecture Overview + +### Phase 1: Simple Container Architecture + +``` +┌─────────────────────────────────────────┐ +│ n8n-MCP Docker Image (Simple) │ +├─────────────────────────────────────────┤ +│ Layer 1: Base Runtime │ +│ - Node.js 20 Alpine (minimal) │ +│ - Essential system dependencies │ +├─────────────────────────────────────────┤ +│ Layer 2: Application │ +│ - Production npm packages │ +│ - Pre-built JavaScript from TypeScript │ +├─────────────────────────────────────────┤ +│ Layer 3: Data & Config │ +│ - SQLite database with auto-init │ +│ - Environment-based configuration │ +└─────────────────────────────────────────┘ +``` + +### Phase 2: Enhanced Architecture (Optional) + +``` +┌─────────────────────────────────────────┐ +│ n8n-MCP Docker Image (Enhanced) │ +├─────────────────────────────────────────┤ +│ Layer 1: Extended Runtime │ +│ - Node.js 20 Alpine │ +│ - nginx (optional via USE_NGINX) │ +│ - supervisor (when nginx enabled) │ +├─────────────────────────────────────────┤ +│ Layer 2: Application │ +│ - Production npm packages │ +│ - Pre-built JavaScript │ +├─────────────────────────────────────────┤ +│ Layer 3: Security & Config │ +│ - Auto-generated SSL certificates │ +│ - nginx configurations │ +│ - Enhanced security headers │ +└─────────────────────────────────────────┘ +``` + +### Dual Mode Operation + +``` + ┌─────────────────┐ + │ Docker Container │ + └────────┬─────────┘ + │ + ┌─────────────┴─────────────┐ + │ Check MCP_MODE env var │ + └─────────────┬─────────────┘ + │ + ┌─────────────────┴─────────────────┐ + │ │ + [stdio mode] [http mode] + │ │ + ▼ ▼ +┌───────────────┐ ┌─────────────────────┐ +│ MCP Server │ │ Check USE_NGINX │ +│ (stdio) │ └──────────┬──────────┘ +└───────────────┘ │ + │ ┌───────────┴───────────┐ + │ │ │ + │ [USE_NGINX=false] [USE_NGINX=true] + │ │ │ + │ ▼ ▼ + │ ┌─────────────┐ ┌──────────────┐ + │ │ Node.js │ │ supervisor │ + │ │ HTTP :3000 │ ├──────┬───────┤ + │ └─────────────┘ │nginx │Node.js│ + │ │:443 │:3000 │ + │ └──────┴───────┘ + ▼ │ +┌───────────────┐ ┌───────────────┐ +│ Claude Desktop│ │ mcp-remote │ +└───────────────┘ └───────────────┘ +``` + +## Implementation Plan + +### Phase 1: Simple Docker Implementation + +#### 1.1 Basic Multi-Stage Dockerfile + +```dockerfile +# Stage 1: Dependencies +FROM node:20-alpine AS deps +WORKDIR /app +COPY package*.json ./ +# Install all dependencies including dev for building +RUN npm ci + +# Stage 2: Builder +FROM node:20-alpine AS builder +WORKDIR /app +COPY package*.json ./ +COPY --from=deps /app/node_modules ./node_modules +COPY . . +# Build TypeScript +RUN npm run build +# Rebuild database during image build (if possible) +RUN npm run rebuild || echo "Database will be initialized at runtime" + +# Stage 3: Simple Runtime +FROM node:20-alpine AS runtime +WORKDIR /app + +# Install only essential tools +RUN apk add --no-cache curl && \ + rm -rf /var/cache/apk/* + +# Install production dependencies only +COPY package*.json ./ +RUN npm ci --only=production && \ + npm cache clean --force + +# Copy built application +COPY --from=builder /app/dist ./dist +COPY --from=builder /app/data ./data 2>/dev/null || mkdir -p ./data + +# Copy necessary files +COPY .env.example .env.example +COPY LICENSE LICENSE +COPY README.md README.md + +# Add container labels +LABEL org.opencontainers.image.source="https://github.com/czlonkowski/n8n-mcp" +LABEL org.opencontainers.image.description="n8n MCP Server - Simple Version" +LABEL org.opencontainers.image.licenses="Sustainable-Use-1.0" +LABEL org.opencontainers.image.vendor="n8n-mcp" +LABEL org.opencontainers.image.title="n8n-mcp" + +# Create data directory and fix permissions +RUN mkdir -p /app/data && \ + addgroup -g 1001 -S nodejs && \ + adduser -S nodejs -u 1001 && \ + chown -R nodejs:nodejs /app + +# Copy entrypoint script +COPY docker/docker-entrypoint.sh /usr/local/bin/ +RUN chmod +x /usr/local/bin/docker-entrypoint.sh + +# Switch to non-root user +USER nodejs + +# Expose HTTP port +EXPOSE 3000 + +# Health check +HEALTHCHECK --interval=30s --timeout=10s --start-period=10s --retries=3 \ + CMD curl -f http://127.0.0.1:3000/health || exit 1 + +# Entrypoint +ENTRYPOINT ["/usr/local/bin/docker-entrypoint.sh"] +CMD ["node", "dist/mcp/index.js"] +``` + +#### 1.2 Simple Entrypoint Script + +```bash +#!/bin/sh +set -e + +# Environment variable validation +if [ "$MCP_MODE" = "http" ] && [ -z "$AUTH_TOKEN" ]; then + echo "ERROR: AUTH_TOKEN is required for HTTP mode" + exit 1 +fi + +# Database initialization with file locking to prevent race conditions +if [ ! -f "/app/data/nodes.db" ]; then + echo "Database not found. Initializing..." + # Use a lock file to prevent multiple containers from initializing simultaneously + ( + flock -x 200 + # Double-check inside the lock + if [ ! -f "/app/data/nodes.db" ]; then + echo "Initializing database..." + cd /app && node dist/scripts/rebuild.js || { + echo "ERROR: Database initialization failed" + exit 1 + } + fi + ) 200>/app/data/.db.lock +fi + +# Fix permissions if running as root (for development) +if [ "$(id -u)" = "0" ]; then + echo "Running as root, fixing permissions..." + chown -R nodejs:nodejs /app/data + # Switch to nodejs user + exec su-exec nodejs "$@" +fi + +# Trap signals for graceful shutdown +trap 'echo "Shutting down..."; kill -TERM $PID' TERM INT + +# Execute the main command in background +"$@" & +PID=$! +wait $PID +``` + +#### 1.3 .dockerignore File + +```dockerignore +# .dockerignore +node_modules +npm-debug.log +.git +.gitignore +.env +.env.local +data/nodes.db +data/*.db +dist +.DS_Store +*.log +coverage +.nyc_output +.vscode +.idea +*.swp +*.swo +*~ +``` + +#### 1.4 Simple docker-compose.yml + +```yaml +# docker-compose.yml +version: '3.8' + +services: + n8n-mcp: + image: ghcr.io/czlonkowski/n8n-mcp:latest + container_name: n8n-mcp + restart: unless-stopped + + # Environment configuration + environment: + # Mode configuration + MCP_MODE: ${MCP_MODE:-http} + AUTH_TOKEN: ${AUTH_TOKEN:?AUTH_TOKEN is required for HTTP mode} + + # Application settings + NODE_ENV: ${NODE_ENV:-production} + LOG_LEVEL: ${LOG_LEVEL:-info} + PORT: ${PORT:-3000} + + # Database + NODE_DB_PATH: ${NODE_DB_PATH:-/app/data/nodes.db} + REBUILD_ON_START: ${REBUILD_ON_START:-false} + + # Volumes for persistence + volumes: + - n8n-mcp-data:/app/data + + # Port mapping + ports: + - "${PORT:-3000}:3000" + + # Resource limits + deploy: + resources: + limits: + memory: 512M + reservations: + memory: 256M + + # Health check + healthcheck: + test: ["CMD", "curl", "-f", "http://127.0.0.1:3000/health"] + interval: 30s + timeout: 10s + retries: 3 + start_period: 40s + +# Named volume for data persistence +volumes: + n8n-mcp-data: + driver: local +``` + +#### 1.5 Environment Variable Documentation + +```bash +# .env.example +# n8n-MCP Docker Configuration + +# Mode configuration +# - stdio: For Claude Desktop integration +# - http: For remote access via HTTP/HTTPS +MCP_MODE=http + +# Authentication token for HTTP mode (required) +# Generate with: openssl rand -base64 32 +AUTH_TOKEN=your-secure-token-here + +# Server configuration +PORT=3000 +NODE_ENV=production +LOG_LEVEL=info + +# Database configuration +NODE_DB_PATH=/app/data/nodes.db +REBUILD_ON_START=false + +# Optional: For future nginx support +USE_NGINX=false +``` + +#### 1.6 Docker-specific Environment Template + +```bash +# .env.docker +# Docker-specific environment template +# Copy to .env and fill in values + +# Required for HTTP mode +AUTH_TOKEN= + +# Server configuration +PORT=3000 +HTTP_PORT=80 +HTTPS_PORT=443 + +# Application settings +NODE_ENV=production +LOG_LEVEL=info +MCP_MODE=http + +# Database +NODE_DB_PATH=/app/data/nodes.db +REBUILD_ON_START=false + +# Optional nginx mode +USE_NGINX=false +``` + +#### 1.7 Development Override File + +```yaml +# docker-compose.override.yml +# Local development overrides (git-ignored) +version: '3.8' + +services: + n8n-mcp: + environment: + NODE_ENV: development + LOG_LEVEL: debug + REBUILD_ON_START: "true" + volumes: + # Mount source for hot reload + - ./src:/app/src:ro + - ./scripts:/app/scripts:ro +``` + +### Phase 2: Enhanced Docker with nginx (Week 2) + +#### 2.1 Enhanced Dockerfile with nginx Support + +```dockerfile +# Dockerfile.nginx - Enhanced version with HTTPS support +FROM node:20-alpine AS deps +WORKDIR /app +COPY package*.json ./ +RUN npm ci + +FROM node:20-alpine AS builder +WORKDIR /app +COPY package*.json ./ +COPY --from=deps /app/node_modules ./node_modules +COPY . . +RUN npm run build +RUN npm run rebuild || echo "Database will be initialized at runtime" + +FROM node:20-alpine AS runtime +WORKDIR /app + +# Install nginx, supervisor, openssl +RUN apk add --no-cache nginx supervisor openssl curl su-exec && \ + rm -rf /var/cache/apk/* + +# Copy application +COPY package*.json ./ +RUN npm ci --only=production && npm cache clean --force +COPY --from=builder /app/dist ./dist +COPY --from=builder /app/data ./data 2>/dev/null || mkdir -p ./data + +# Setup users and directories +RUN addgroup -g 1001 -S nodejs && \ + adduser -S nodejs -u 1001 && \ + mkdir -p /app/data /app/certs /var/log/supervisor /var/log/nginx \ + /var/cache/nginx /var/run /etc/nginx/conf.d && \ + chown -R nodejs:nodejs /app && \ + chown -R nginx:nginx /var/log/nginx /var/cache/nginx + +# Add container labels +LABEL org.opencontainers.image.source="https://github.com/czlonkowski/n8n-mcp" +LABEL org.opencontainers.image.description="n8n MCP Server - nginx Enhanced Version" +LABEL org.opencontainers.image.licenses="Sustainable-Use-1.0" +LABEL org.opencontainers.image.vendor="n8n-mcp" +LABEL org.opencontainers.image.title="n8n-mcp-nginx" + +# Copy configurations +COPY docker/ /docker/ +RUN chmod +x /docker/docker-entrypoint-nginx.sh + +# Generate self-signed certificate +RUN openssl req -x509 -nodes -days 365 -newkey rsa:2048 \ + -keyout /app/certs/server.key \ + -out /app/certs/server.crt \ + -subj "/C=US/ST=State/L=City/O=n8n-MCP/CN=localhost" && \ + chown -R nodejs:nodejs /app/certs + +EXPOSE 80 443 3000 + +HEALTHCHECK --interval=30s --timeout=10s --start-period=10s --retries=3 \ + CMD /docker/healthcheck.sh + +ENTRYPOINT ["/docker/docker-entrypoint-nginx.sh"] +``` + +#### 2.2 nginx Configuration (Simplified) + +```nginx +# /docker/nginx.conf +user nginx; +worker_processes auto; +error_log /var/log/nginx/error.log warn; +pid /var/run/nginx.pid; + +events { + worker_connections 1024; +} + +http { + include /etc/nginx/mime.types; + default_type application/octet-stream; + + log_format main '$remote_addr - $remote_user [$time_local] "$request" ' + '$status $body_bytes_sent "$http_referer" ' + '"$http_user_agent" "$http_x_forwarded_for"'; + access_log /var/log/nginx/access.log main; + + sendfile on; + tcp_nopush on; + tcp_nodelay on; + keepalive_timeout 65; + server_tokens off; + client_max_body_size 1m; + + # Rate limiting + limit_req_zone $binary_remote_addr zone=mcp_limit:10m rate=10r/s; + + upstream mcp_backend { + server 127.0.0.1:3000; + } + + server { + listen 80; + listen [::]:80; + return 301 https://$host$request_uri; + } + + server { + listen 443 ssl http2; + listen [::]:443 ssl http2; + + ssl_certificate /app/certs/server.crt; + ssl_certificate_key /app/certs/server.key; + ssl_protocols TLSv1.2 TLSv1.3; + ssl_ciphers HIGH:!aNULL:!MD5; + ssl_prefer_server_ciphers on; + + # Security headers + add_header X-Frame-Options "DENY" always; + add_header X-Content-Type-Options "nosniff" always; + add_header X-XSS-Protection "1; mode=block" always; + add_header Strict-Transport-Security "max-age=31536000" always; + + # Rate limiting + limit_req zone=mcp_limit burst=20 nodelay; + + location /mcp { + # Let Node.js handle auth - nginx just proxies + proxy_pass http://mcp_backend; + proxy_http_version 1.1; + proxy_set_header Upgrade $http_upgrade; + proxy_set_header Connection 'upgrade'; + 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; + proxy_set_header Authorization $http_authorization; + proxy_cache_bypass $http_upgrade; + proxy_read_timeout 60s; + } + + location /health { + proxy_pass http://mcp_backend/health; + access_log off; + } + + location / { + return 301 /health; + } + } +} +``` + +#### 2.3 Supervisor Configuration with Log Rotation + +```ini +# /docker/supervisord.conf +[supervisord] +nodaemon=true +user=root +logfile=/var/log/supervisor/supervisord.log +pidfile=/var/run/supervisord.pid +logfile_maxbytes=10MB +logfile_backups=3 + +[program:nginx] +command=/usr/sbin/nginx -g "daemon off;" +autostart=true +autorestart=true +stdout_logfile=/var/log/supervisor/nginx.log +stderr_logfile=/var/log/supervisor/nginx.error.log +stdout_logfile_maxbytes=10MB +stdout_logfile_backups=3 +stderr_logfile_maxbytes=10MB +stderr_logfile_backups=3 +priority=10 + +[program:mcp-server] +command=su-exec nodejs node /app/dist/mcp/index.js +directory=/app +autostart=true +autorestart=true +environment=NODE_ENV="production" +stdout_logfile=/var/log/supervisor/mcp.log +stderr_logfile=/var/log/supervisor/mcp.error.log +stdout_logfile_maxbytes=10MB +stdout_logfile_backups=3 +stderr_logfile_maxbytes=10MB +stderr_logfile_backups=3 +priority=20 + +[group:mcp] +programs=nginx,mcp-server +``` + +#### 2.4 Enhanced docker-compose.nginx.yml + +```yaml +# docker-compose.nginx.yml - For HTTPS deployments +version: '3.8' + +services: + n8n-mcp: + image: ghcr.io/czlonkowski/n8n-mcp:nginx + container_name: n8n-mcp-https + restart: unless-stopped + + environment: + MCP_MODE: http + AUTH_TOKEN: ${AUTH_TOKEN:?AUTH_TOKEN is required} + USE_NGINX: "true" + NODE_ENV: ${NODE_ENV:-production} + LOG_LEVEL: ${LOG_LEVEL:-info} + + volumes: + - n8n-mcp-data:/app/data + # Optional: Custom certificates + # - ./certs/server.crt:/app/certs/server.crt:ro + # - ./certs/server.key:/app/certs/server.key:ro + + ports: + - "${HTTP_PORT:-80}:80" + - "${HTTPS_PORT:-443}:443" + + # Resource limits + deploy: + resources: + limits: + memory: 768M + reservations: + memory: 512M + + healthcheck: + test: ["CMD", "/docker/healthcheck.sh"] + interval: 30s + timeout: 10s + retries: 3 + +volumes: + n8n-mcp-data: + driver: local +``` + +#### 2.5 Enhanced Entrypoint for nginx Mode + +```bash +#!/bin/sh +# /docker/docker-entrypoint-nginx.sh +set -e + +# Environment variable validation +if [ "$MCP_MODE" = "http" ] && [ -z "$AUTH_TOKEN" ]; then + echo "ERROR: AUTH_TOKEN is required for HTTP mode" + exit 1 +fi + +# Database initialization with locking +if [ ! -f "/app/data/nodes.db" ]; then + ( + flock -x 200 + if [ ! -f "/app/data/nodes.db" ]; then + echo "Initializing database..." + su-exec nodejs node /app/dist/scripts/rebuild.js || exit 1 + fi + ) 200>/app/data/.db.lock +fi + +# Fix permissions +chown -R nodejs:nodejs /app/data + +if [ "$USE_NGINX" = "true" ] && [ "$MCP_MODE" = "http" ]; then + echo "Starting with nginx HTTPS support..." + exec /usr/bin/supervisord -c /docker/supervisord.conf +else + echo "Starting Node.js directly..." + # Trap signals for graceful shutdown + trap 'echo "Shutting down..."; kill -TERM $PID' TERM INT + + su-exec nodejs node /app/dist/mcp/index.js & + PID=$! + wait $PID +fi +``` + +#### 2.6 Health Check Script + +```bash +#!/bin/sh +# /docker/healthcheck.sh + +if [ "$USE_NGINX" = "true" ]; then + # Check nginx first + curl -f -k https://127.0.0.1/health || \ + curl -f http://127.0.0.1:3000/health || exit 1 +else + # Direct Node.js check + curl -f http://127.0.0.1:3000/health || exit 1 +fi +``` + + +### Phase 3: CI/CD & Documentation (Week 3) + +#### 3.1 GitHub Actions Workflow + +```yaml +# .github/workflows/docker-build.yml +name: Build and Push Docker Images + +on: + push: + branches: + - main + tags: + - 'v*' + pull_request: + branches: + - main + workflow_dispatch: + +env: + REGISTRY: ghcr.io + IMAGE_NAME: ${{ github.repository }} + +jobs: + build-simple: + name: Build Simple Docker Image + runs-on: ubuntu-latest + permissions: + contents: read + packages: write + + steps: + - name: Checkout repository + uses: actions/checkout@v4 + + - name: Set up QEMU + uses: docker/setup-qemu-action@v3 + + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@v3 + + - name: Log in to GitHub Container Registry + uses: docker/login-action@v3 + with: + registry: ${{ env.REGISTRY }} + username: ${{ github.actor }} + password: ${{ secrets.GITHUB_TOKEN }} + + - name: Extract metadata + id: meta + uses: docker/metadata-action@v5 + with: + images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }} + tags: | + type=ref,event=branch + type=ref,event=pr + type=semver,pattern={{version}} + type=semver,pattern={{major}}.{{minor}} + type=sha,prefix={{branch}}-,format=short + type=raw,value=latest,enable={{is_default_branch}} + + - name: Build and push simple Docker image + uses: docker/build-push-action@v5 + with: + context: . + file: ./Dockerfile + platforms: linux/amd64,linux/arm64 + push: ${{ github.event_name != 'pull_request' }} + tags: ${{ steps.meta.outputs.tags }} + labels: ${{ steps.meta.outputs.labels }} + cache-from: type=gha + cache-to: type=gha,mode=max + + build-nginx: + name: Build nginx-enhanced Docker Image + runs-on: ubuntu-latest + permissions: + contents: read + packages: write + + steps: + - name: Checkout repository + uses: actions/checkout@v4 + + - name: Set up QEMU + uses: docker/setup-qemu-action@v3 + + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@v3 + + - name: Log in to GitHub Container Registry + if: github.event_name != 'pull_request' + uses: docker/login-action@v3 + with: + registry: ${{ env.REGISTRY }} + username: ${{ github.actor }} + password: ${{ secrets.GITHUB_TOKEN }} + + - name: Extract metadata + id: meta + uses: docker/metadata-action@v5 + with: + images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }} + flavor: | + suffix=-nginx + tags: | + type=ref,event=branch + type=ref,event=pr + type=semver,pattern={{version}} + type=raw,value=nginx,enable={{is_default_branch}} + + - name: Build and push nginx Docker image + uses: docker/build-push-action@v5 + with: + context: . + file: ./Dockerfile.nginx + platforms: linux/amd64,linux/arm64 + push: ${{ github.event_name != 'pull_request' }} + tags: ${{ steps.meta.outputs.tags }} + labels: ${{ steps.meta.outputs.labels }} + cache-from: type=gha + cache-to: type=gha,mode=max +``` + +#### 3.2 Quick Start Documentation + +```markdown +# Docker Quick Start Guide + +## Option 1: Simple HTTP Server (Recommended to Start) + +1. Create a `.env` file: +```bash +# Required for HTTP mode +AUTH_TOKEN=$(openssl rand -base64 32) +echo "AUTH_TOKEN=$AUTH_TOKEN" > .env +``` + +2. Run with Docker Compose: +```bash +docker compose up -d +``` + +3. Test the server: +```bash +curl http://localhost:3000/health +``` + +4. Configure Claude Desktop with mcp-remote: +```json +{ + "mcpServers": { + "n8n-remote": { + "command": "npx", + "args": [ + "-y", + "@modelcontextprotocol/mcp-remote@latest", + "connect", + "http://localhost:3000/mcp" + ], + "env": { + "MCP_AUTH_TOKEN": "your-auth-token-here" + } + } + } +} +``` + +## Option 2: Local stdio Mode (Claude Desktop Direct) + +```json +{ + "mcpServers": { + "n8n-docker": { + "command": "docker", + "args": [ + "run", + "--rm", + "-i", + "-e", "MCP_MODE=stdio", + "-v", "n8n-mcp-data:/app/data", + "ghcr.io/czlonkowski/n8n-mcp:latest" + ] + } + } +} +``` + +## Option 3: HTTPS with nginx (Production) + +```bash +# Use the nginx-enhanced image +docker compose -f docker-compose.nginx.yml up -d +``` + +Access via HTTPS: https://your-server/mcp +``` + +## Implementation Details + +### Critical Issues Addressed + +#### 1. nginx Auth Token Validation +Instead of trying to validate tokens in nginx configuration, we: +- Let nginx act as a simple proxy +- Pass Authorization header to Node.js +- Node.js handles all authentication logic +- This avoids nginx string interpolation issues + +#### 2. Database Race Condition Prevention +Implemented file locking in entrypoint script: +```bash +( + flock -x 200 + if [ ! -f "/app/data/nodes.db" ]; then + echo "Initializing database..." + su-exec nodejs node /app/dist/scripts/rebuild.js || exit 1 + fi +) 200>/app/data/.db.lock +``` + +#### 3. Volume Permissions +- Use `su-exec` for proper user switching +- Fix permissions at runtime in entrypoint +- Consistent nodejs user (UID 1001) + +#### 4. Simplified Architecture +- Phase 1: Simple Node.js-only Docker image +- Phase 2: Optional nginx enhancement +- Users choose based on their needs + +#### 5. Essential Files Structure +``` +docker/ +├── docker-entrypoint.sh # Simple entrypoint +├── docker-entrypoint-nginx.sh # Enhanced entrypoint +├── nginx.conf # Main nginx config +├── supervisord.conf # Process manager config +└── healthcheck.sh # Health check script +``` + +### Testing Strategy + +#### Test Script for Docker Deployment + +```bash +#!/bin/bash +# scripts/test-docker.sh + +echo "🧪 Testing n8n-MCP Docker Deployment" + +# Test 1: Build simple image +echo "1. Building simple Docker image..." +docker build -t n8n-mcp:test . + +# Test 2: Test stdio mode +echo "2. Testing stdio mode..." +echo '{"jsonrpc":"2.0","method":"tools/list","id":1}' | \ + docker run --rm -i -e MCP_MODE=stdio n8n-mcp:test + +# Test 3: Test HTTP mode +echo "3. Testing HTTP mode..." +docker run -d --name test-http \ + -e MCP_MODE=http \ + -e AUTH_TOKEN=test-token \ + -p 3001:3000 \ + n8n-mcp:test + +sleep 5 + +# Check health +curl -f http://localhost:3001/health || echo "Health check failed" + +# Test auth +curl -H "Authorization: Bearer test-token" \ + -H "Content-Type: application/json" \ + -d '{"jsonrpc":"2.0","method":"tools/list","id":1}' \ + http://localhost:3001/mcp + +docker stop test-http && docker rm test-http + +# Test 4: Volume persistence +echo "4. Testing volume persistence..." +docker volume create test-data +docker run -d --name test-persist \ + -v test-data:/app/data \ + -e MCP_MODE=http \ + -e AUTH_TOKEN=test \ + -p 3002:3000 \ + n8n-mcp:test + +sleep 10 +docker exec test-persist ls -la /app/data/nodes.db +docker stop test-persist && docker rm test-persist +docker volume rm test-data + +echo "✅ Docker tests completed!" +``` + +## Success Metrics + +1. **Ease of Use**: Setup time < 2 minutes +2. **Performance**: < 5 second startup time +3. **Compatibility**: Works on Linux, macOS, Windows (Docker Desktop) +4. **Reliability**: Automatic database initialization +5. **Security**: Built-in auth token validation + +## Implementation Checklist + +### Week 1: Phase 1 - Simple Docker +- [ ] Create `Dockerfile` with labels +- [ ] Create `docker/docker-entrypoint.sh` with validation and graceful shutdown +- [ ] Create `.dockerignore` +- [ ] Create `docker-compose.yml` with resource limits +- [ ] Create `.env.docker` template +- [ ] Create `scripts/test-docker.sh` +- [ ] Test both stdio and HTTP modes +- [ ] Update README with Docker quick start + +### Week 2: Phase 2 - Enhanced Features +- [ ] Create `Dockerfile.nginx` with labels +- [ ] Create `docker/docker-entrypoint-nginx.sh` +- [ ] Create `docker/nginx.conf` +- [ ] Create `docker/supervisord.conf` with log rotation +- [ ] Create `docker/healthcheck.sh` +- [ ] Create `docker-compose.nginx.yml` with resource limits +- [ ] Test HTTPS functionality +- [ ] Document SSL certificate options + +### Week 3: Phase 3 - CI/CD & Polish +- [ ] Set up GitHub Actions workflow +- [ ] Configure GHCR permissions +- [ ] Test multi-architecture builds +- [ ] Create Docker quick start guide +- [ ] Add `docker-compose.override.yml` to `.gitignore` +- [ ] Tag first release +- [ ] Create troubleshooting guide +- [ ] Gather community feedback + +## Key Decisions + +### 1. Progressive Enhancement Strategy +- Start with simple Node.js-only image +- Add nginx as optional enhancement +- Let users choose complexity level + +### 2. Authentication Strategy +- Node.js handles all auth validation +- nginx acts as simple proxy +- Avoids complex nginx configurations + +### 3. File Structure +``` +. +├── Dockerfile # Simple version +├── Dockerfile.nginx # Enhanced version +├── docker-compose.yml # Simple deployment +├── docker-compose.nginx.yml # HTTPS deployment +├── .dockerignore +└── docker/ + ├── docker-entrypoint.sh + ├── docker-entrypoint-nginx.sh + ├── nginx.conf + ├── supervisord.conf + └── healthcheck.sh +``` + +### 4. Image Tags +- `ghcr.io/czlonkowski/n8n-mcp:latest` - Simple version +- `ghcr.io/czlonkowski/n8n-mcp:nginx` - nginx-enhanced +- `ghcr.io/czlonkowski/n8n-mcp:v1.0.0` - Version tags + +## Conclusion + +This revised Docker deployment plan addresses all critical issues identified in the review while maintaining a pragmatic, phased approach. By starting simple and progressively adding features, we ensure that users can quickly get started while still having access to production-grade features when needed. The plan prioritizes ease of use, security, and maintainability. \ No newline at end of file