docker-compose.yml•22.8 kB
#version: "3.9" # Supported by both podman-compose and Docker Compose v2+
###############################################################################
# NETWORKS + VOLUMES - declared first so they can be referenced later
###############################################################################
networks:
mcpnet: # Single user-defined bridge network keeps traffic private
driver: bridge
volumes: # Named volumes survive podman-compose down/up
pgdata:
mariadbdata:
mysqldata:
mongodata:
pgadmindata:
redisinsight_data:
###############################################################################
# CORE SERVICE - MCP Gateway
###############################################################################
services:
# ──────────────────────────────────────────────────────────────────────
# MCP Gateway - the main API server for the MCP stack
# ──────────────────────────────────────────────────────────────────────
gateway:
image: ${IMAGE_LOCAL:-mcpgateway/mcpgateway:latest} # Use 0.5.0 - works with ARM64 emulation
#image: ghcr.io/ibm/mcp-context-forge:0.7.0 # Use the release MCP Context Forge image (x86-64-v3 only)
#image: ${IMAGE_LOCAL:-mcpgateway/mcpgateway:latest} # Use the local latest image. Run `make docker-prod` to build it.
#build:
# context: .
# dockerfile: Containerfile # Same one the Makefile builds
restart: unless-stopped
ports:
- "4444:4444" # HTTP (or HTTPS if SSL=true is set)
networks: [mcpnet]
# ──────────────────────────────────────────────────────────────────────
# Environment - pick ONE database URL line, comment the rest
# ──────────────────────────────────────────────────────────────────────
environment:
- HOST=0.0.0.0
- PORT=4444
- DATABASE_URL=postgresql://postgres:${POSTGRES_PASSWORD:-mysecretpassword}@postgres:5432/mcp
# - DATABASE_URL=mysql+pymysql://mysql:${MYSQL_PASSWORD:-changeme}@mysql:3306/mcp
# - DATABASE_URL=mysql+pymysql://admin:${MARIADB_PASSWORD:-changeme}@mariadb:3306/mcp
# - DATABASE_URL=mongodb://admin:${MONGO_PASSWORD:-changeme}@mongodb:27017/mcp
- CACHE_TYPE=redis # backend for caching (memory, redis, database, or none)
- REDIS_URL=redis://redis:6379/0
# JWT Configuration - Choose ONE approach:
# Option 1: HMAC (Default - Simple deployments)
- JWT_ALGORITHM=HS256
- JWT_SECRET_KEY=my-test-key
# Option 2: RSA (Production - Asymmetric, uncomment and generate certs)
# - JWT_ALGORITHM=RS256
# - JWT_PUBLIC_KEY_PATH=/app/certs/jwt/public.pem
# - JWT_PRIVATE_KEY_PATH=/app/certs/jwt/private.pem
- JWT_AUDIENCE=mcpgateway-api
- JWT_ISSUER=mcpgateway
- EMAIL_AUTH_ENABLED=true
- PLATFORM_ADMIN_EMAIL=admin@example.com
- PLATFORM_ADMIN_PASSWORD=changeme
- REQUIRE_TOKEN_EXPIRATION=false
- MCPGATEWAY_UI_ENABLED=true
- MCPGATEWAY_ADMIN_API_ENABLED=true
# Security configuration (using defaults)
- ENVIRONMENT=development
- SECURITY_HEADERS_ENABLED=true
- CORS_ALLOW_CREDENTIALS=true
# - SSL=true
# - CERT_FILE=/app/certs/cert.pem
# - KEY_FILE=/app/certs/key.pem
# Phoenix Observability Integration (uncomment when using Phoenix)
# - PHOENIX_ENDPOINT=${PHOENIX_ENDPOINT:-http://phoenix:6006}
# - OTEL_EXPORTER_OTLP_ENDPOINT=${OTEL_EXPORTER_OTLP_ENDPOINT:-http://phoenix:4317}
# - OTEL_SERVICE_NAME=${OTEL_SERVICE_NAME:-mcp-gateway}
# - OTEL_TRACES_EXPORTER=${OTEL_TRACES_EXPORTER:-otlp}
# - OTEL_METRICS_EXPORTER=${OTEL_METRICS_EXPORTER:-otlp}
# - OTEL_RESOURCE_ATTRIBUTES=${OTEL_RESOURCE_ATTRIBUTES:-deployment.environment=docker,service.namespace=mcp}
depends_on: # Default stack: Postgres + Redis + Alembic migration
postgres:
condition: service_healthy # ▶ wait for DB
redis:
condition: service_started
# migration:
# condition: service_completed_successfully
healthcheck:
test: ["CMD", "python3", "-c", "import urllib.request; import json; resp = urllib.request.urlopen('http://localhost:4444/health', timeout=5); data = json.loads(resp.read()); exit(0 if data.get('status') == 'healthy' else 1)"]
#test: ["CMD", "curl", "-f", "https://localhost:4444/health"]
interval: 30s
timeout: 10s
retries: 5
start_period: 30s
# volumes:
# - ./certs:/app/certs:ro # mount certs folder read-only
###############################################################################
# DATABASES - enable ONE of these blocks and adjust DATABASE_URL
###############################################################################
postgres: # Official image - easy defaults
image: postgres:17
environment:
- POSTGRES_USER=postgres
- POSTGRES_PASSWORD=mysecretpassword
- POSTGRES_DB=mcp
volumes:
- pgdata:/var/lib/postgresql/data
networks: [mcpnet]
healthcheck:
test: ["CMD-SHELL", "pg_isready -U $$POSTGRES_USER"]
interval: 30s
timeout: 5s
retries: 5
start_period: 20s
# mariadb:
# image: mariadb:11
# environment:
# - MARIADB_ROOT_PASSWORD=mysecretpassword
# - MARIADB_DATABASE=mcp
# - MARIADB_USER=admin
# - MARIADB_PASSWORD=changeme
# volumes: [mariadbdata:/var/lib/mysql]
# networks: [mcpnet]
# mysql:
# image: mysql:8
# environment:
# - MYSQL_ROOT_PASSWORD=mysecretpassword
# - MYSQL_DATABASE=mcp
# - MYSQL_USER=mysql
# - MYSQL_PASSWORD=changeme
# volumes: [mysqldata:/var/lib/mysql]
# networks: [mcpnet]
# mongodb:
# image: mongo:7
# environment:
# - MONGO_INITDB_ROOT_USERNAME=admin
# - MONGO_INITDB_ROOT_PASSWORD=changeme
# - MONGO_INITDB_DATABASE=mcp
# volumes: [mongodata:/data/db]
# networks: [mcpnet]
# migration:
# #image: ghcr.io/ibm/mcp-context-forge:0.5.0 # Use the release MCP Context Forge image
# image: mcpgateway/mcpgateway:latest # Use the local latest image. Run `make docker-prod` to build it.
# build:
# context: .
# dockerfile: Containerfile
# environment:
# - DATABASE_URL=postgresql://postgres:${POSTGRES_PASSWORD:-mysecretpassword}@postgres:5432/mcp
# command: alembic upgrade head
# depends_on:
# postgres:
# condition: service_healthy
# networks: [mcpnet]
###############################################################################
# CACHE
###############################################################################
redis:
image: redis:latest
ports:
- "6379:6379" # expose only if you want host access
networks: [mcpnet]
###############################################################################
# OPTIONAL ADMIN TOOLS - handy web UIs for DB & cache (disabled by default)
###############################################################################
pgadmin: # 🔧 Postgres admin UI
image: dpage/pgadmin4:latest
environment:
- PGADMIN_DEFAULT_EMAIL=admin@example.com
- PGADMIN_DEFAULT_PASSWORD=changeme
ports:
- "5050:80" # http://localhost:5050
volumes:
- pgadmindata:/var/lib/pgadmin
networks: [mcpnet]
depends_on:
postgres:
condition: service_healthy
# ──────────────────────────────────────────────────────────────────────
# Redis Insight - a powerful Redis GUI (recently updated)
# ──────────────────────────────────────────────────────────────────────
redis_insight: # 🔧 Redis Insight GUI
image: redis/redisinsight:latest
container_name: redisinsight
restart: unless-stopped
networks: [mcpnet]
ports:
- "5540:5540" # Redis Insight UI (default 5540)
depends_on: # Default stack: Postgres + Redis
redis:
condition: service_started
# ──────────────────────────────────────────────────────────────────────
# Persist data (config, logs, history) between restarts
# ──────────────────────────────────────────────────────────────────────
# volumes:
# - ./redisinsight_data:/data
volumes:
- redisinsight_data:/data # <- persist data in named volume
# ──────────────────────────────────────────────────────────────────────
# Preconfigure Redis connection(s) via env vars
# ──────────────────────────────────────────────────────────────────────
environment:
# Single connection (omit "*" since only one):
- RI_REDIS_HOST=redis # <- your Redis hostname
- RI_REDIS_PORT=6379 # <- your Redis port
- RI_REDIS_USERNAME=default # <- ACL/username (Redis 6+)
#- RI_REDIS_PASSWORD=changeme # <- Redis AUTH password
#- RI_REDIS_TLS=true # <- enable TLS
# Optional: validate self-signed CA instead of trusting all:
# - RI_REDIS_TLS_CA_PATH=/certs/selfsigned.crt
# - RI_REDIS_TLS_CERT_PATH=/certs/client.crt
# - RI_REDIS_TLS_KEY_PATH=/certs/client.key
# - RI_REDIS_TLS=true # (already set above)
# ──────────────────────────────────────────────────────────────────
# Core Redis Insight settings
# ──────────────────────────────────────────────────────────────────
- RI_APP_HOST=0.0.0.0 # <- listen on all interfaces
- RI_APP_PORT=5540 # <- UI port (container-side)
# ──────────────────────────────────────────────────────────────────
# (Optional) Enable HTTPS for the UI
# ──────────────────────────────────────────────────────────────────
# - RI_SERVER_TLS_KEY=/certs/tls.key
# - RI_SERVER_TLS_CERT=/certs/tls.crt
# ──────────────────────────────────────────────────────────────────────
# Redis Commander - a web-based Redis GUI
# ──────────────────────────────────────────────────────────────────────
# redis_commander: # 🔧 Redis key browser
# image: rediscommander/redis-commander:latest
# restart: unless-stopped
# networks: [mcpnet]
# depends_on:
# redis:
# condition: service_started
# ports:
# - "8081:8081" # <- change if you want a different host port
# # ─────────────────────────────────────────────────────────────────────────
# # Mount your local certs directory (only needed if you want real cert validation)
# # ─────────────────────────────────────────────────────────────────────────
# # volumes:
# # - ./certs:/certs:ro # <- put your selfsigned.crt (PEM) in ./certs
# environment:
# # ──────────────────────────────────────────────────────────────────────
# # LEGACY HOST LIST (for showing in UI - not used for TLS)
# # ──────────────────────────────────────────────────────────────────────
# - REDIS_HOSTS=local:redis:6379
# # ──────────────────────────────────────────────────────────────────────
# # CORE REDIS/TLS
# # ──────────────────────────────────────────────────────────────────────
# - REDIS_HOST=redis # <- your Redis hostname or IP
# - REDIS_PORT=6379 # <- your Redis port
# - REDIS_USERNAME=admin # ← REQUIRED when Redis has users/ACLs
# - REDIS_PASSWORD=${REDIS_PASSWORD}# <- if you need a Redis auth password
# # - REDIS_TLS=true # <- turn on TLS
# - CLUSTER_NO_TLS_VALIDATION=true # <- skip SNI/hostname checks in clusters
# # ──────────────────────────────────────────────────────────────────────
# # SELF-SIGNED: trust no-CA by default
# # ──────────────────────────────────────────────────────────────────────
# - NODE_TLS_REJECT_UNAUTHORIZED=0 # <- Node.js will accept your self-signed cert
# # ──────────────────────────────────────────────────────────────────────
# # HTTP BASIC-AUTH FOR THE WEB UI
# # ──────────────────────────────────────────────────────────────────────
# - HTTP_USER=admin # <- change your UI username
# - HTTP_PASSWORD=changeme # <- change your UI password
# # ──────────────────────────────────────────────────────────────────────
# # OPTIONAL: ENABLE REAL CERT VALIDATION (instead of skipping checks)
# # ──────────────────────────────────────────────────────────────────────
# # - REDIS_TLS_CA_CERT_FILE=/certs/selfsigned.crt
# # - REDIS_TLS_SERVER_NAME=redis.example.com
# mongo_express: # 🔧 MongoDB GUI (works if mongodb service is enabled)
# image: mongo-express:1
# environment:
# - ME_CONFIG_MONGODB_ADMINUSERNAME=admin
# - ME_CONFIG_MONGODB_ADMINPASSWORD=changeme
# - ME_CONFIG_MONGODB_SERVER=mongodb
# ports:
# - "8082:8081" # http://localhost:8082
# networks: [mcpnet]
# depends_on:
# mongodb:
# condition: service_started
# phpmyadmin: # 🔧 MySQL / MariaDB GUI
# image: phpmyadmin:latest
# environment:
# - PMA_HOST=mysql # or mariadb
# - PMA_USER=mysql
# - PMA_PASSWORD=changeme
# - PMA_ARBITRARY=1 # allow login to any host if you switch DBs
# ports:
# - "8083:80" # http://localhost:8083
# networks: [mcpnet]
# depends_on:
# mysql:
# condition: service_started
###############################################################################
# OPTIONAL MCP SERVERS - drop-in helpers the Gateway can call
###############################################################################
# ###############################################################################
# # Fast Time Server - High-performance time/timezone service for MCP
# ###############################################################################
# fast_time_server:
# image: ghcr.io/ibm/fast-time-server:latest
# restart: unless-stopped
# networks: [mcpnet]
# ports:
# - "8888:8080" # Map host port 8888 to container port 8080
# command: ["-transport=sse", "-listen=0.0.0.0", "-port=8080", "-log-level=info"]
# ###############################################################################
# # Auto-registration service - registers fast_time_server with gateway
# ###############################################################################
# register_fast_time:
# image: python:3.11-slim
# networks: [mcpnet]
# depends_on:
# gateway:
# condition: service_healthy
# fast_time_server:
# condition: service_started
# environment:
# - JWT_SECRET_KEY=my-test-key
# # This is a one-shot container that exits after registration
# restart: "no"
# entrypoint: ["/bin/sh", "-c"]
# command:
# - |
# echo "Installing MCP Context Forge Gateway..."
# pip install --quiet mcp-contextforge-gateway
# echo "Installing curl..."
# apt-get update -qq && apt-get install -y -qq curl
# echo "Waiting for services to be ready..."
# # Wait for fast_time_server to be ready
# for i in {1..30}; do
# if curl -s -f http://fast_time_server:8080/health > /dev/null 2>&1; then
# echo "✅ fast_time_server is healthy"
# break
# fi
# echo "Waiting for fast_time_server... ($$i/30)"
# sleep 2
# done
# echo "Generating JWT token..."
# export MCPGATEWAY_BEARER_TOKEN=$$(python3 -m mcpgateway.utils.create_jwt_token -u admin --secret my-test-key)
# echo "Registering fast_time_server with gateway..."
# RESPONSE=$$(curl -s -X POST http://gateway:4444/gateways \
# -H "Authorization: Bearer $$MCPGATEWAY_BEARER_TOKEN" \
# -H "Content-Type: application/json" \
# -d '{"name":"fast_time","url":"http://fast_time_server:8080/sse"}')
# echo "Registration response: $$RESPONSE"
# # Check if registration was successful
# if echo "$$RESPONSE" | grep -q '"id"'; then
# echo "✅ Successfully registered fast_time_server"
# # Optional: Create a virtual server with the time tools
# echo "Creating virtual server..."
# curl -s -X POST http://gateway:4444/servers \
# -H "Authorization: Bearer $$MCPGATEWAY_BEARER_TOKEN" \
# -H "Content-Type: application/json" \
# -d '{"name":"time_server","description":"Fast time tools","associatedTools":["1","2"]}' || true
# echo "✅ Setup complete!"
# else
# echo "❌ Registration failed"
# exit 1
# fi
###############################################################################
# IBM i MCP Server - SQL database operations for IBM i systems
# https://github.com/IBM/ibmi-mcp-server.git
###############################################################################
ibmi-mcp-server:
image: ${IBMI_MCP_IMAGE:-ibmi-mcp-server:latest} # Use env var or default to local image
build:
context: . # Build from current directory
dockerfile: ./Dockerfile # Explicit path to Dockerfile
container_name: ibmi-mcp-server
networks: [mcpnet]
ports:
- "3010:3010" # Map host port 3010 to container port 3010
restart: unless-stopped
env_file:
- .env # Load environment variables from .env file
environment:
# Enable YAML auto-reload for development (file watching)
- YAML_AUTO_RELOAD=true
volumes:
- ./prebuiltconfigs:/usr/src/app/prebuiltconfigs:rw # Mount prebuilt configurations with read-write for auto-reload
- ./secrets:/usr/src/app/secrets:ro # Mount secrets directory as read-only
healthcheck:
test: ["CMD", "curl", "-f", "http://localhost:3010/health"]
interval: 30s
timeout: 10s
retries: 5
start_period: 20s
###############################################################################
# Hashicorp Terraform MCP Server
# https://hub.docker.com/r/hashicorp/terraform-mcp-server
# https://github.com/hashicorp/terraform-mcp-server/blob/main/README.md
###############################################################################
# terraform-mcp-server:
# image: docker.io/hashicorp/terraform-mcp-server:dev
# container_name: terraform-mcp-server
# networks: [mcpnet]
# ports:
# - "8001:8080" # Map host port 8888 to container port 8080
# restart: unless-stopped
# environment:
# - TRANSPORT_MODE=streamable-http
# - TRANSPORT_HOST=0.0.0.0
# - TRANSPORT_PORT=8080
# - MCP_CORS_MODE=disabled
# healthcheck:
# test: ["CMD", "curl", "-f", "http://localhost:8080/health"]
# interval: 30s
# timeout: 10s
# retries: 5
# start_period: 20s