Kubernetes

Kubernetes deployment guide with resource definitions, example manifests, persistent volumes, and secrets management.

This guide covers deploying the MCP Hub Platform on Kubernetes. It provides example manifests for all components and guidance on production configuration.

Prerequisites

RequirementMinimum Version
Kubernetes1.27+
kubectlMatching cluster version
Helm (optional)3.12+
Storage ClassWith dynamic provisioning
Ingress ControllerNginx, Traefik, or similar

Architecture on Kubernetes

The MCP Hub Platform maps to the following Kubernetes resources:

ComponentResource TypeReplicasNotes
hub-webDeployment + Service2+Stateless, horizontally scalable
hub-ingestion-workerDeployment1+Stateless, scales with ingestion load
hub-results-workerDeployment1+Stateless, scales with analysis throughput
scan-workerDeployment2+CPU-intensive, scale based on analysis queue
registryDeployment + Service2+Stateless, horizontally scalable
postgresStatefulSet1 (or managed)Use managed database in production
redisStatefulSet1 (or managed)Use managed Redis in production
minioStatefulSet1 (or managed)Use managed S3 in production
lavinmqStatefulSet1 (or managed)AMQP broker

Namespace

Create a dedicated namespace:

apiVersion: v1
kind: Namespace
metadata:
  name: mcp-hub
kubectl apply -f namespace.yaml

Secrets

Store sensitive configuration in Kubernetes Secrets:

apiVersion: v1
kind: Secret
metadata:
  name: mcp-hub-secrets
  namespace: mcp-hub
type: Opaque
stringData:
  DATABASE_URL: "postgres://mcphub:STRONG_PASSWORD@postgres:5432/mcphub?sslmode=require"
  REDIS_URL: "redis://redis:6390"
  AMQP_URL: "amqp://guest:STRONG_PASSWORD@lavinmq:5672/"
  S3_ACCESS_KEY_ID: "your-access-key"
  S3_SECRET_ACCESS_KEY: "your-secret-key"
  AUTH0_CLIENT_SECRET: "your-auth0-secret"
  SESSION_SECRET: "your-session-secret-minimum-32-characters-long"
  REGISTRY_SERVICE_TOKEN: "your-registry-service-token"
  STRIPE_SECRET_KEY: "sk_live_..."
  STRIPE_WEBHOOK_SECRET: "whsec_..."

ConfigMap

Non-sensitive configuration shared across services:

apiVersion: v1
kind: ConfigMap
metadata:
  name: mcp-hub-config
  namespace: mcp-hub
data:
  S3_ENDPOINT: "http://minio:9000"
  S3_REGION: "us-east-1"
  S3_BUCKET_SOURCES: "mcp-hub-sources"
  S3_BUCKET_ANALYSIS: "mcp-hub-analysis"
  S3_USE_PATH_STYLE: "true"
  AMQP_EXCHANGE: "mcp.jobs"
  LOG_LEVEL: "info"
  REGISTRY_URL: "http://registry:8081"
  AUTH0_DOMAIN: "your-tenant.auth0.com"
  AUTH0_CLIENT_ID: "your-client-id"
  AUTH0_CALLBACK_URL: "https://hub.yourdomain.com/auth/callback"
  SCAN_MODE: "deep"
  SCAN_TIMEOUT: "30m"
  MAX_CONCURRENT: "5"

Hub Web Deployment

apiVersion: apps/v1
kind: Deployment
metadata:
  name: hub-web
  namespace: mcp-hub
  labels:
    app: hub-web
spec:
  replicas: 2
  selector:
    matchLabels:
      app: hub-web
  template:
    metadata:
      labels:
        app: hub-web
    spec:
      containers:
        - name: hub-web
          image: your-registry/mcp-hub:latest
          command: ["web"]
          ports:
            - containerPort: 8080
              name: http
          envFrom:
            - configMapRef:
                name: mcp-hub-config
            - secretRef:
                name: mcp-hub-secrets
          env:
            - name: SERVER_PORT
              value: "8080"
            - name: SKIP_MIGRATIONS
              value: "true"
          resources:
            requests:
              cpu: 500m
              memory: 256Mi
            limits:
              cpu: "2"
              memory: 1Gi
          livenessProbe:
            httpGet:
              path: /health
              port: 8080
            initialDelaySeconds: 10
            periodSeconds: 15
          readinessProbe:
            httpGet:
              path: /health
              port: 8080
            initialDelaySeconds: 5
            periodSeconds: 10
---
apiVersion: v1
kind: Service
metadata:
  name: hub-web
  namespace: mcp-hub
spec:
  selector:
    app: hub-web
  ports:
    - port: 8080
      targetPort: 8080
      name: http
  type: ClusterIP

Hub Workers

Ingestion Worker

apiVersion: apps/v1
kind: Deployment
metadata:
  name: hub-ingestion-worker
  namespace: mcp-hub
  labels:
    app: hub-ingestion-worker
spec:
  replicas: 1
  selector:
    matchLabels:
      app: hub-ingestion-worker
  template:
    metadata:
      labels:
        app: hub-ingestion-worker
    spec:
      containers:
        - name: ingestion-worker
          image: your-registry/mcp-hub:latest
          command: ["ingest-worker"]
          envFrom:
            - configMapRef:
                name: mcp-hub-config
            - secretRef:
                name: mcp-hub-secrets
          env:
            - name: WORKER_WORKSPACE
              value: "/workspace"
            - name: WORKER_MAX_CONCURRENT
              value: "10"
          resources:
            requests:
              cpu: 250m
              memory: 256Mi
            limits:
              cpu: "1"
              memory: 512Mi
          volumeMounts:
            - name: workspace
              mountPath: /workspace
      volumes:
        - name: workspace
          emptyDir:
            sizeLimit: 5Gi

Results Worker

apiVersion: apps/v1
kind: Deployment
metadata:
  name: hub-results-worker
  namespace: mcp-hub
  labels:
    app: hub-results-worker
spec:
  replicas: 1
  selector:
    matchLabels:
      app: hub-results-worker
  template:
    metadata:
      labels:
        app: hub-results-worker
    spec:
      containers:
        - name: results-worker
          image: your-registry/mcp-hub:latest
          command: ["results-worker"]
          envFrom:
            - configMapRef:
                name: mcp-hub-config
            - secretRef:
                name: mcp-hub-secrets
          resources:
            requests:
              cpu: 250m
              memory: 256Mi
            limits:
              cpu: "1"
              memory: 512Mi

Scan Worker

The scan worker is CPU-intensive and benefits from dedicated resource allocation:

apiVersion: apps/v1
kind: Deployment
metadata:
  name: scan-worker
  namespace: mcp-hub
  labels:
    app: scan-worker
spec:
  replicas: 2
  selector:
    matchLabels:
      app: scan-worker
  template:
    metadata:
      labels:
        app: scan-worker
    spec:
      containers:
        - name: scan-worker
          image: your-registry/mcp-scan:latest
          command: ["worker"]
          envFrom:
            - configMapRef:
                name: mcp-hub-config
            - secretRef:
                name: mcp-hub-secrets
          env:
            - name: MCP_SCAN_WORKER_HEALTH_PORT
              value: "8083"
          ports:
            - containerPort: 8083
              name: health
          resources:
            requests:
              cpu: "1"
              memory: 1Gi
            limits:
              cpu: "4"
              memory: 4Gi
          livenessProbe:
            httpGet:
              path: /healthz
              port: 8083
            initialDelaySeconds: 10
            periodSeconds: 30
          readinessProbe:
            httpGet:
              path: /healthz
              port: 8083
            initialDelaySeconds: 5
            periodSeconds: 10
          volumeMounts:
            - name: scan-workspace
              mountPath: /workspace
      volumes:
        - name: scan-workspace
          emptyDir:
            sizeLimit: 10Gi

Registry

apiVersion: apps/v1
kind: Deployment
metadata:
  name: registry
  namespace: mcp-hub
  labels:
    app: registry
spec:
  replicas: 2
  selector:
    matchLabels:
      app: registry
  template:
    metadata:
      labels:
        app: registry
    spec:
      containers:
        - name: registry
          image: your-registry/mcp-registry:latest
          ports:
            - containerPort: 8081
              name: http
          env:
            - name: MCP_REGISTRY_SERVER_LISTEN
              value: ":8081"
            - name: MCP_REGISTRY_DB_DSN
              valueFrom:
                secretKeyRef:
                  name: mcp-hub-secrets
                  key: DATABASE_URL
            - name: MCP_REGISTRY_STORAGE_TYPE
              value: "s3"
            - name: MCP_REGISTRY_STORAGE_S3_BUCKET
              value: "mcp-registry"
            - name: MCP_REGISTRY_STORAGE_S3_ENDPOINT
              valueFrom:
                configMapKeyRef:
                  name: mcp-hub-config
                  key: S3_ENDPOINT
            - name: MCP_REGISTRY_AUTH_MODE
              value: "oss"
            - name: MCP_REGISTRY_PUBLIC_READ_CATALOG
              value: "true"
            - name: MCP_REGISTRY_PUBLIC_DOWNLOAD_ARTIFACTS
              value: "true"
          envFrom:
            - secretRef:
                name: mcp-hub-secrets
          resources:
            requests:
              cpu: 250m
              memory: 256Mi
            limits:
              cpu: "1"
              memory: 512Mi
          livenessProbe:
            httpGet:
              path: /healthz
              port: 8081
            initialDelaySeconds: 5
            periodSeconds: 10
          readinessProbe:
            httpGet:
              path: /healthz
              port: 8081
            initialDelaySeconds: 3
            periodSeconds: 5
---
apiVersion: v1
kind: Service
metadata:
  name: registry
  namespace: mcp-hub
spec:
  selector:
    app: registry
  ports:
    - port: 8081
      targetPort: 8081
      name: http
  type: ClusterIP

Database Migration Job

Run migrations as a Kubernetes Job before deploying application services:

apiVersion: batch/v1
kind: Job
metadata:
  name: hub-migrate
  namespace: mcp-hub
spec:
  backoffLimit: 3
  template:
    spec:
      restartPolicy: OnFailure
      containers:
        - name: migrate
          image: your-registry/mcp-hub:latest
          command: ["migrate", "up"]
          envFrom:
            - secretRef:
                name: mcp-hub-secrets
          env:
            - name: LOG_LEVEL
              value: "info"

Ingress

Expose the hub dashboard and registry through an Ingress:

apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: mcp-hub-ingress
  namespace: mcp-hub
  annotations:
    cert-manager.io/cluster-issuer: letsencrypt-prod
spec:
  tls:
    - hosts:
        - hub.yourdomain.com
        - registry.yourdomain.com
      secretName: mcp-hub-tls
  rules:
    - host: hub.yourdomain.com
      http:
        paths:
          - path: /
            pathType: Prefix
            backend:
              service:
                name: hub-web
                port:
                  number: 8080
    - host: registry.yourdomain.com
      http:
        paths:
          - path: /
            pathType: Prefix
            backend:
              service:
                name: registry
                port:
                  number: 8081

Persistent Volumes

If running PostgreSQL and MinIO as StatefulSets (development/testing only):

apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: postgres-data
  namespace: mcp-hub
spec:
  accessModes:
    - ReadWriteOnce
  resources:
    requests:
      storage: 20Gi
  storageClassName: standard

---
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: minio-data
  namespace: mcp-hub
spec:
  accessModes:
    - ReadWriteOnce
  resources:
    requests:
      storage: 50Gi
  storageClassName: standard

Horizontal Pod Autoscaler

Scale scan workers based on CPU usage:

apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata:
  name: scan-worker-hpa
  namespace: mcp-hub
spec:
  scaleTargetRef:
    apiVersion: apps/v1
    kind: Deployment
    name: scan-worker
  minReplicas: 2
  maxReplicas: 10
  metrics:
    - type: Resource
      resource:
        name: cpu
        target:
          type: Utilization
          averageUtilization: 70

Deployment Order

Deploy resources in this order:

  1. Namespace and Secrets: namespace.yaml, secrets.yaml, configmap.yaml
  2. Infrastructure: PostgreSQL, Redis, MinIO, LavinMQ (or configure managed services)
  3. Migrations: Run the hub-migrate Job
  4. Registry: Deploy the registry Deployment and Service
  5. Application: Deploy hub-web, hub-ingestion-worker, hub-results-worker
  6. Workers: Deploy scan-worker
  7. Networking: Apply Ingress rules
  8. Autoscaling: Apply HPA configurations

Production Checklist

  • Replace all default passwords and tokens with strong randomly generated values
  • Use managed database (RDS, Cloud SQL) instead of StatefulSet PostgreSQL
  • Use managed S3 (AWS S3, GCS) instead of MinIO
  • Use managed Redis (ElastiCache, Memorystore) instead of StatefulSet
  • Configure TLS via Ingress with cert-manager
  • Set appropriate resource requests and limits for all pods
  • Configure HPA for scan-worker and registry
  • Set up monitoring with Prometheus and Grafana
  • Configure backup strategy for PostgreSQL
  • Use External Secrets Operator for secret management
  • Enable network policies to restrict inter-service communication
  • Set up log aggregation (ELK, Loki, CloudWatch)