Skip to content

πŸš€ Deploying the MCP Gateway Stack with Helm

This guide walks you through installing, upgrading, and removing the full MCP Gateway Stack using Helm. The stack includes:

  • 🧠 MCP Context Forge (the gateway)
  • πŸ—„ PostgreSQL database
  • ⚑ Redis cache
  • πŸ§‘πŸ’» PgAdmin UI (optional)
  • 🧰 Redis Commander UI (optional)

Everything is deployable via Helm on any Kubernetes cluster (Minikube, kind, EKS, AKS, GKE, OpenShift, etc.).

πŸ“¦ Helm chart location: https://github.com/IBM/mcp-context-forge/tree/main/charts/mcp-stack


🧭 Architecture

flowchart TD
    subgraph Ingress Layer
        ingress[NGINX Ingress Controller]
    end

    subgraph Application Layer
        mcp[MCP Context Forge]
        pgadmin[PgAdmin UI<br/>optional]
        rediscommander[Redis Commander UI<br/>optional]
    end

    subgraph Data Layer
        postgres[(PostgreSQL)]
        redis[(Redis)]
    end

    ingress --> mcp
    ingress --> pgadmin
    ingress --> rediscommander

    mcp --> postgres
    mcp --> redis

    pgadmin --> postgres
    rediscommander --> redis

πŸ“‹ Prerequisites

Requirement Notes
Kubernetes β‰₯ 1.23 Local (Minikube/kind) or managed (EKS, AKS, GKE, etc.)
Helm 3 Used for installing and managing releases
kubectl Configured to talk to your target cluster
Ingress Controller NGINX, Traefik, or cloud-native (or disable via values.yaml)
StorageClass (RWX) Required for PostgreSQL PVC unless persistence is disabled
βœ… Pre-flight Checklist (Run Before Deploying)

Ensure these checks pass before installing the stack.

kubectl config current-context
kubectl cluster-info

Verify you're pointing to the intended cluster context.

kubectl auth can-i create namespace
kubectl auth can-i create deployment -n default
kubectl auth can-i create clusterrolebinding

Confirm you have adequate access (or switch to a namespace where you do).

kubectl version -o json | jq -r '.serverVersion.gitVersion'

Must be v1.23 or higher. Some Helm charts and features depend on it.

kubectl get sc

Required for persistent volumes (e.g., PostgreSQL).

kubectl get pods -A | grep -E 'ingress|traefik|nginx' || echo "No ingress controller found"

If not, deploy one or disable ingress in values.yaml.


πŸ›  Install Helm & kubectl

You'll need both Helm and kubectl installed to deploy the stack.

brew install helm kubernetes-cli

Uses Homebrew to install both tools in one step.

# Helm
curl -fsSL https://raw.githubusercontent.com/helm/helm/main/scripts/get-helm-3 | bash

# kubectl
curl -LO "https://dl.k8s.io/release/$(curl -sSL https://dl.k8s.io/release/stable.txt)/bin/linux/amd64/kubectl"
chmod +x kubectl
sudo mv kubectl /usr/local/bin

Installs latest stable versions directly from official sources.

choco install -y kubernetes-helm kubernetes-cli

Requires Chocolatey to be installed first.

helm version
kubectl version
kubectl config get-contexts

Confirm both tools are installed and kubectl is configured for your cluster.

πŸ“¦ Clone and inspect the chart
git clone https://github.com/IBM/mcp-context-forge.git
cd mcp-context-forge/charts/mcp-stack
helm lint .

βœ… RBAC test (if enabled)

Confirm the service account created by the chart can access resources as expected.

kubectl auth can-i list pods \
  --as=system:serviceaccount:mcp-private:mcp-stack-sa \
  -n mcp-private
kubectl get role,rolebinding -n mcp-private
kubectl describe role mcp-stack-role -n mcp-private

πŸ” Prepare the Namespace (Recommended)

It's best practice to isolate the stack in its own namespace, with labels and policies for security and clarity.

kubectl create namespace mcp-private --dry-run=client -o yaml | kubectl apply -f -

You can use a different name (e.g. mcp, prod-gateway) as long as you reference it consistently in your Helm commands.

kubectl label namespace mcp-private environment=prod --overwrite
kubectl annotate namespace mcp-private "config.kubernetes.io/owner=mcp" --overwrite

Labels and annotations can help with GitOps sync, audit, and tracking tools.

cat <<'EOF' | kubectl apply -n mcp-private -f -
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: deny-by-default
spec:
  podSelector: {}
  policyTypes: [Ingress, Egress]
EOF

This restricts all traffic by default. You'll need to define allowed communication between components separately, or use service mesh policies.

kubectl get ns mcp-private
kubectl get networkpolicy -n mcp-private
kubectl get sa default -n mcp-private -o yaml

Confirm that the namespace exists and basic policies are in place.

🧾 Customize values

🧾 Customize values.yaml

Copy and edit the default values file to tailor the deployment to your environment.

cp values.yaml my-values.yaml

This gives you a working copy of the Helm chart and lets you customize settings safely.

mcpContextForge:
  image:
    repository: ghcr.io/ibm/mcp-context-forge
    tag: v1.0.0
  ingress:
    enabled: true
    host: gateway.local        # Change this to your actual DNS
    className: nginx

  envFrom:
    - secretRef:
        name: mcp-gateway-secret
    - configMapRef:
        name: mcp-gateway-config

postgres:
  credentials:
    user: admin
    password: S3cuReP@ss       # Avoid hardcoding in production
  persistence:
    size: 10Gi

pgadmin:
  enabled: false

redisCommander:
  enabled: false

rbac:
  create: true

This configures image version, ingress host, secrets, storage, and RBAC. In production, prefer secrets over inline passwords.

helm lint .

Ensures the chart is valid and ready to install.


πŸš€ Install / Upgrade the stack

πŸš€ Install or Upgrade the Stack

Install the MCP Gateway Stack into your Kubernetes cluster using Helm. This will deploy all components defined in your my-values.yaml.

helm upgrade --install mcp-stack . \
  --namespace mcp-private \
  --create-namespace=false \
  -f my-values.yaml \
  --wait --timeout 30m --debug

This installs or upgrades the stack in the mcp-private namespace, using your custom values file. Set --create-namespace=true if the namespace hasn't been created yet.

  • MCP Context Forge (API Gateway)
  • PostgreSQL with optional persistence
  • Redis cache
  • (Optional) PgAdmin & Redis Commander
  • Ingress configuration (if enabled)
  • NetworkPolicy and RBAC (if configured)

Helm upgrades are idempotent. You can run the same command again safely after making changes to my-values.yaml.


βœ… Verify deployment

βœ… Verify Deployment

After installation completes, confirm that all resources are running and healthy.

kubectl get all -n mcp-private
helm status mcp-stack -n mcp-private

This should show running pods, services, deployments, and Helm release status.

kubectl get ingress -n mcp-private
curl http://gateway.local/health

You should see a 200 OK response or similar from the health endpoint.

kubectl port-forward svc/mcp-stack-app 8080:80 -n mcp-private
curl http://localhost:8080/health

Port-forwarding gives you local access to the service when Ingress is disabled or not ready.

kubectl logs -n mcp-private deploy/mcp-stack-app --tail=50
kubectl describe pod -l app.kubernetes.io/instance=mcp-stack -n mcp-private

Useful for debugging if components are not responding or entering CrashLoopBackOff.


πŸ”„ Upgrade & Rollback

πŸ”„ Upgrade & Rollback

You can upgrade to a new image version, preview changes before applying, or roll back to a previous release.

helm upgrade mcp-stack . -n mcp-private \
  --set mcpContextForge.image.tag=v1.2.3 \
  --wait

Updates only the image tag (or any specific value you override) while preserving existing resources.

helm plugin install https://github.com/databus23/helm-diff
helm diff upgrade mcp-stack . -n mcp-private -f my-values.yaml

Shows what will change without applying anything. Requires the helm-diff plugin.

helm rollback mcp-stack 1 -n mcp-private

Use helm history mcp-stack -n mcp-private to list available revisions before rolling back.


🧹 Uninstall

🧹 Uninstall the Stack

This removes all components deployed by the Helm chart.

helm uninstall mcp-stack -n mcp-private

Removes deployments, services, and related resources created by the chart.

kubectl delete namespace mcp-private

Use this if you want to fully clean up everything, including secrets, configmaps, and PVCs.

# Uninstall the Helm release
helm uninstall mcp-stack -n mcp-private

# Delete PVCs if you're not keeping data
kubectl delete pvc --all -n mcp-private

# Delete the namespace
kubectl delete namespace mcp-private

# Reinstall from scratch (if desired)
helm upgrade --install mcp-stack . \
  --namespace mcp-private \
  -f my-values.yaml \
  --wait --timeout 15m --debug

Use this flow when you need to wipe the environment and redeploy fresh.


πŸ§ͺ CI/CD: Packaging & OCI Push

πŸ§ͺ CI/CD: Packaging & OCI Push

Package your Helm chart and push it to an OCI-compliant registry for use in GitOps workflows (e.g., Argo CD, Flux).

helm lint .
helm package . -d dist/

This validates your chart and creates a .tgz package in the dist/ directory.

helm push dist/mcp-stack-*.tgz oci://ghcr.io/<your-org>/charts

Replace <your-org> with your GitHub container registry org. Make sure HELM_EXPERIMENTAL_OCI=1 is set if using older Helm versions.

  • Works with private registries
  • Easily versioned and managed
  • Supported by Argo CD and Flux natively

Reference the chart by OCI URL in your GitOps tool:

oci://ghcr.io/your-org/charts/mcp-stack

Then sync as usual using your preferred tool.


🧯 Troubleshooting

🧯 Troubleshooting Common Issues

Quick fixes and diagnostic tips for common deployment problems.

  • Cause: Image not found or access denied
  • Fix:
    kubectl describe pod -n mcp-private
    
  • Check image: field in values.yaml
  • Ensure the image tag exists and is publicly accessible (or add a pull secret)
  • Cause: Ingress host mismatch or controller not ready
  • Fix:
    kubectl get ingress -n mcp-private
    kubectl get svc -A | grep ingress
    
  • Make sure the ingress hostname matches DNS or /etc/hosts
  • Confirm an Ingress Controller is deployed and available
  • Fix:
    kubectl logs -n mcp-private <pod-name>
    kubectl describe pod <pod-name> -n mcp-private
    

Check logs for configuration or secret injection issues.

  • Cause: Secret or ConfigMap not mounted
  • Fix: Confirm envFrom is configured in your my-values.yaml and the resources exist:
kubectl get secret mcp-gateway-secret -n mcp-private
kubectl get configmap mcp-gateway-config -n mcp-private
  • Fix: Ensure RBAC roles are created properly:
    kubectl get role,rolebinding -n mcp-private
    

You can also enable auto-RBAC creation with:

rbac:
  create: true

🧾 values.yaml - Common Keys

🧾 values.yaml - Common Keys Reference

Most frequently used keys in values.yaml and what they control.

Key Default Description
mcpContextForge.image.tag latest Image version for MCP Context Forge
mcpContextForge.ingress.enabled true Enables ingress resource creation
mcpContextForge.ingress.host gateway.local Hostname used in Ingress (change in production)
mcpContextForge.service.type ClusterIP Use LoadBalancer if running in cloud
mcpContextForge.envFrom [] Allows mounting Secrets/ConfigMaps as env vars
postgres.credentials.user admin Default DB username (use secret in prod)
postgres.credentials.password test123 Default DB password (avoid hardcoding)
postgres.persistence.enabled true Enables persistent volume claim for PostgreSQL
postgres.persistence.size 10Gi Size of the PostgreSQL volume
pgadmin.enabled false Enable PgAdmin for DB UI
redisCommander.enabled false Enable Redis Commander for Redis UI
rbac.create true Automatically create Role/RoleBinding

πŸ“ For all possible options, see the full values.yaml file in the chart repository.

See full annotations in values.yaml.


πŸ“š Further Reading

πŸ“š Further Reading & References

Useful links to understand Helm, Kubernetes, and GitOps tools used with the MCP stack.


βœ… You now have a production-ready Helm workflow for MCP Context Forge. It's CI-friendly, customizable, and tested across Kubernetes distributions.