TL;DR: A self-hosted MCP gateway deployment benefits from three architectural decisions: separating the control plane from the data plane, managing credentials with an external secrets store instead of native Kubernetes Secrets, and enforcing policy-as-code in the request path before the model is involved. This guide provides production-tested manifests for network policies, PodDisruptionBudgets, probes, and OPA policy enforcement, plus readiness requirements you can hand to platform engineering. Composio's Enterprise tier offers patterns for this deployment model, with SOC 2 Type II and ISO/IEC 27001:2022 certifications backing the security review.
Your enterprise prospect just asked where credentials are stored, and "we hash them in the application layer" is not passing the security review. A self-hosted MCP gateway on Kubernetes puts credentials in an AES-256 encrypted vault isolated from application code. Establishing the control plane and data plane separation early is a key design decision.
A recurring failure pattern in self-hosted deployments is treating policy enforcement as a prompt problem instead of an infrastructure problem. This guide is a production reference, not a tutorial. It covers the three architectural decisions that determine whether your deployment survives its first enterprise security review, with manifest templates and operational guidance you can hand directly to platform engineering.
Building for data residency and compliance
The deployment model decision comes before any YAML. For organizations in healthcare, finance, or government where regulated data must remain within specific boundaries, the deployment model is often a legal requirement before any technical comparison begins.
Local data sovereignty requirements
An MCP gateway on-prem keeps credential material, tool invocation payloads, and audit logs inside infrastructure you control. No request body, token, or response transits a vendor's cloud. If your data residency requirement says customer data cannot leave your VPC or your datacenter, a managed gateway is disqualified before the technical evaluation starts.
Meeting industry regulatory requirements
The EU AI Act requires organizations deploying AI in high-risk decisions to implement automatic logging capabilities that support post-hoc auditability (Regulation (EU) 2024/1689, Article 12), and HIPAA adds a hard constraint: you cannot disclose PHI (Protected Health Information) to an external service without a BAA (Business Associate Agreement) in place (45 CFR § 164.308(b)(1)). Self-hosting puts the audit trail, and the obligation, inside your boundary. However, if your compliance requirement includes a signed BAA with your MCP infrastructure vendor, confirm BAA availability directly with the Composio account team before treating self-hosting as your HIPAA solution, as no BAA has been publicly documented for Composio. Composio's MCP gateway governance breakdown documents how that logging maps to EU AI Act and HIPAA audit requirements.
Air-gapped Kubernetes cluster support
Self-hosted deployment is designed to support air-gapped clusters with no external calls required at runtime, but confirm the current maturity of air-gapped patterns with your Composio account team before committing this to a production architecture. Composio's Enterprise tier offers patterns for air-gapped deployments so you can mirror everything to your internal registry. Requirements: mirror all container images, vendor the charts, and pre-stage connector definitions before deploy time.
MCP gateway operational costs and trade-offs
The trade-off is operational. Self-hosting means your platform team owns Kubernetes operations, patching, and scaling, while a managed service shifts infrastructure responsibility to the vendor under a shared responsibility model.
Dimension | Self-hosted gateway | Managed service |
|---|---|---|
Data residency | Full control, on-prem or VPC | Transits vendor infrastructure |
Air-gapped | Designed to support (confirm current patterns with account team) | Not applicable |
Maintenance burden | Your platform team owns it | Vendor-managed SLA |
Initial setup | Weeks of initial setup | Hours to minutes |
Technical blueprint for Kubernetes MCP deployments
A common mistake in self-hosted AI agent infrastructure is collapsing the control plane and data plane into one deployment. Retrofitting the separation after go-live means re-architecting request routing, policy enforcement placement, and credential handling across live workloads, work that can span multiple sprints.
Control plane vs data plane roles
An MCP control plane is the governance layer for fleets of MCP servers: it manages server discovery, authenticates and attests each server, enforces which tools an agent is allowed to call, and produces tamper-evident logs of every tool invocation. The data plane is the execution layer where tool calls run: MCP servers, proxies, and credential resolution. This pattern borrows from well-understood control plane separations in distributed systems like clusters and service meshes.
Kubernetes pod responsibility mapping
Pod role | Plane | Responsibility |
|---|---|---|
Policy engine (OPA) | Control | Allow/deny evaluation in request path |
Registry/discovery | Control | MCP server catalog and attestation |
Audit logger | Control | Tamper-evident tool invocation logs |
MCP gateway proxy | Data | Request routing, credential injection |
MCP servers | Data | Tool execution against external APIs |
The 2026-07-28 MCP specification's stateless HTTP transport means your gateway proxy tier can scale horizontally without session affinity, provided clients are using that transport mode — the earlier 2025-11-25 Streamable HTTP transport required session affinity due to the initialize handshake and Mcp-Session-Id header. That simplifies autoscaling, but it also means every request must carry enough context for the policy engine to evaluate it independently.
Environment and cluster readiness steps
Before any manifest ships, verify the cluster against these steps. Each item maps to a documented failure mode that has stalled production launches.
Cluster version: Recommended v1.24 or later for modern Helm-based deployments. If you are deploying the CEL
ValidatingAdmissionPolicyblock in the policy-as-code section, v1.30 or later is required; clusters on v1.24–1.29 must use the OPA/Gatekeeper-based equivalent instead.Resource headroom: Plan capacity for multiple gateway replicas plus control plane pods.
Storage class: Verify a CSI (Container Storage Interface) driver with appropriate access modes for stateful components.
Ingress controller: Confirm an ingress controller is installed and configured.
Secrets backend: Consider an external secrets store reachable from the cluster.
Network policy support: Verify the CNI (Container Network Interface) enforces NetworkPolicy (Calico, Cilium).
Cluster resource headroom for gateway replicas
Size for the steady state plus a node failure. With pod anti-affinity spreading gateway replicas across nodes, losing one node must leave enough headroom for the survivors to absorb shifted traffic while the HPA's (Horizontal Pod Autoscaler) stabilization window settles, or autoscaling will chase its own scaling decisions during failover.
Storage setup for MCP gateways
Stateful components (registries, credential caches) need persistent volumes. Validate dynamic provisioning works before deployment day, because a StatefulSet stuck in Pending on a missing StorageClass is a launch-day incident, not a planning detail.
External access patterns for MCP gateways
Decide early whether agents reach the gateway over internal cluster DNS, a private load balancer, or a public ingress with TLS and OIDC (OpenID Connect). This choice drives your ingress manifests, certificate management, and the network policies in the next sections.
Handling sensitive data in MCP deployments
Kubernetes Secrets have a fundamental problem: they are base64 encoded in manifests and, by default, stored unencrypted in etcd unless you configure encryption at rest. Anyone with RBAC access to read secrets can decode them instantly. Native secrets alone do not meet production audit requirements.
External Secrets Operator for credential management
External Secrets Operator (ESO) solves this by syncing secrets from external providers like HashiCorp Vault, AWS Secrets Manager, or Azure Key Vault into your cluster automatically. Your applications consume standard Kubernetes Secrets while benefiting from Vault's dynamic secrets, detailed audit logs, and centralized policy management.
apiVersion: external-secrets.io/v1
kind: ClusterSecretStore
metadata:
name: vault-backend
spec:
provider:
vault:
server: "https://vault.example.com"
path: "secret"
version: "v2"
auth:
kubernetes:
mountPath: "kubernetes"
role: "external-secrets"
serviceAccountRef:
name: external-secrets
namespace: external-secrets-system
---
apiVersion: external-secrets.io/v1
kind: ExternalSecret
metadata:
name: app-secret
namespace: production
spec:
refreshInterval: 1h
secretStoreRef:
name: vault-backend
kind: ClusterSecretStore
target:
name: app-secret
data:
- secretKey: config
remoteRef:
key: production/app/configVault and Kubernetes ServiceAccount authentication
Never use static AppRole credentials or root tokens in production, because they violate least-privilege principles and create audit findings. Instead, use Kubernetes ServiceAccount authentication, which binds Vault access directly to pod identity and namespace boundaries. This same pattern applies to connector credential injection: credentials land in pod environments through environment variable injection, without ever touching application code or the LLM context.
K8s native secrets management options
Native secrets are acceptable for non-production clusters or low-sensitivity configuration when you enable etcd encryption at rest and tight RBAC. They fall short when you need automated rotation, centralized audit of secret access, or multi-cluster sync, which is why regulated deployments standardize on an external store.
Secret lifecycle and rotation in Kubernetes
Set refreshInterval on every ExternalSecret and test that rotation triggers no service disruption. Implement NetworkPolicies restricting ESO pod egress to only Vault endpoints and Kubernetes API servers, blocking lateral movement if the controller is compromised.
Kubernetes ingress and policy best practices
The moment one egress network policy applies to a pod, the pod is isolated for egress. Start from default deny and open only what the gateway needs.
MCP gateway namespace default-deny policy
Implementing default deny enforces the principle of least privilege:
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: default-deny-all
namespace: mcp-gateway
spec:
podSelector: {}
policyTypes:
- Ingress
- EgressThen allow DNS:
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: allow-dns
namespace: mcp-gateway
spec:
podSelector: {}
policyTypes:
- Egress
egress:
- to:
- namespaceSelector:
matchLabels:
kubernetes.io/metadata.name: kube-system
podSelector:
matchLabels:
k8s-app: kube-dns
ports:
- protocol: UDP
port: 53
- protocol: TCP
port: 53Kubernetes ingress for MCP gateways
Restrict inbound traffic to the ingress controller namespace only:
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: mcp-gateway-ingress
namespace: mcp-gateway
spec:
podSelector:
matchLabels:
app: mcp-gateway
policyTypes:
- Ingress
ingress:
- from:
- namespaceSelector:
matchLabels:
kubernetes.io/metadata.name: ingress-nginx
ports:
- protocol: TCP
port: 8080Once you integrate network policy to control pod connectivity, you need to configure the allow and deny settings in both directions.
TLS termination and mTLS for gateway traffic
Terminate TLS at the ingress with certificates from cert-manager, and require mTLS or a signed internal certificate for control plane to data plane traffic. A production-grade MCP gateway Kubernetes deployment should minimize plaintext hops in the request path.
MCP gateway egress restrictions
The data plane typically needs egress to the external APIs your connectors call and your identity provider. Enumerate both as explicit egress rules. A default-deny posture here is what turns "the agent cannot reach arbitrary endpoints" from a prompt instruction into an infrastructure fact.
Strategies for reliable self-hosted MCP nodes
Without PodDisruptionBudgets, a node drain could take down your entire application. PDBs tell Kubernetes how many pods can be unavailable during voluntary disruptions.
Pod disruption budget best practices
apiVersion: policy/v1
kind: PodDisruptionBudget
metadata:
name: mcp-gateway-pdb
namespace: mcp-gateway
spec:
minAvailable: 2
selector:
matchLabels:
app: mcp-gatewayPair the PDB with anti-affinity so replicas never share a node:
spec:
replicas: 3
template:
spec:
affinity:
podAntiAffinity:
requiredDuringSchedulingIgnoredDuringExecution:
- labelSelector:
matchLabels:
app: mcp-gateway
topologyKey: kubernetes.io/hostnameVoluntary disruptions are planned actions you control (node drains, cluster upgrades), while involuntary disruptions are unplanned (hardware failures, OOM kills), and your PDB only governs the voluntary kind.
K8s autoscaling for MCP gateways
apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata:
name: mcp-gateway-hpa
namespace: mcp-gateway
spec:
scaleTargetRef:
apiVersion: apps/v1
kind: Deployment
name: mcp-gateway
minReplicas: 3
maxReplicas: 10
metrics:
- type: Resource
resource:
name: cpu
target:
type: Utilization
averageUtilization: 70Set minReplicas above your PDB's minAvailable with enough headroom to absorb rolling update surge, so autoscaling never fights your disruption budget during a scale-down.
Cross-region gateway deployment patterns
For multi-region deployments, run an independent gateway stack per region and route with geo-aware DNS. Consider replicating the control plane's registry across regions so a region loss can degrade gracefully rather than taking down policy enforcement globally.
When to use StatefulSets for gateways
Use StatefulSets for stateful MCP components like credential stores and registries, where stable identity and persistent volumes matter:
apiVersion: apps/v1
kind: StatefulSet
metadata:
name: mcp-registry
namespace: mcp-gateway
spec:
serviceName: mcp-registry
replicas: 2
selector:
matchLabels:
app: mcp-registry
template:
metadata:
labels:
app: mcp-registry
spec:
containers:
- name: registry
image: composio/mcp-registry:latest
volumeMounts:
- name: data
mountPath: /data
volumeClaimTemplates:
- metadata:
name: data
spec:
accessModes: ["ReadWriteOnce"]
resources:
requests:
storage: 10GiStateless gateway proxies stay as Deployments. Mixing the two concerns into one workload type is how teams end up with proxies that cannot scale and registries that lose data on reschedule.
Key validation steps for production launch
The validation phase is where you prove the architecture claims before the security reviewer asks.
Distributed tracing for MCP gateway tool calls
Add the OpenTelemetry Operator to enable automatic instrumentation, annotate deployments to inject instrumentation without code changes, and deploy the Collector to gather traces from all nodes:
apiVersion: opentelemetry.io/v1alpha1
kind: OpenTelemetryCollector
metadata:
name: mcp-collector
namespace: mcp-gateway
spec:
config: |
receivers:
otlp:
protocols:
grpc:
endpoint: 0.0.0.0:4317
processors:
batch:
send_batch_size: 1024
exporters:
jaeger:
endpoint: jaeger-collector:14250
service:
pipelines:
traces:
receivers: [otlp]
processors: [batch]
exporters: [jaeger]A trace for a single tool call should show the ingress hop, the policy evaluation span, credential resolution, and the outbound API call. If any span is missing, your audit story has a gap.
Kubernetes health checks for MCP gateways
Kubernetes supports three probe types: startup probes check if the container has started, liveness probes check if it is still running, and readiness probes check if it can accept traffic:
startupProbe:
httpGet:
path: /healthz/startup
port: 8080
periodSeconds: 5
failureThreshold: 60
livenessProbe:
httpGet:
path: /healthz/live
port: 8080
periodSeconds: 15
failureThreshold: 3
readinessProbe:
httpGet:
path: /healthz/ready
port: 8080
periodSeconds: 5
failureThreshold: 3The readiness endpoint must reflect downstream dependency health (secrets backend reachable, policy engine loaded), not just process liveness, or Kubernetes will route traffic to a pod that cannot serve it.
MCP gateway backup and restore
Back up the registry's persistent volumes and your ExternalSecrets definitions, and rehearse a restore into a fresh namespace on a regular cadence. A restored gateway with stale policy definitions is a governance incident, so test that policy state restores exactly, not just that pods come back.
Policy-as-code enforcement for gateway access
This is where policy-as-code separates a governance control from a prompt suggestion. The API gateway is a natural enforcement point because it already sits on the request path and already knows the caller's identity, and what CEL (Common Expression Language) and OPA bring is the ability to express and evaluate policies, not just check role lists.
package mcp.authz
import rego.v1
default allow := false
# Allow tool invocation if user has required role and tool is not restricted
allow if {
input.user.role in ["admin", "agent_operator"]
not restricted_tools[input.tool.name]
input.method == "POST"
}
# Allow read-only tool calls for auditors
allow if {
input.user.role == "auditor"
input.method == "GET"
input.tool.name in ["describe_tools", "list_executions"]
}
# Restrict sensitive financial tools to finance team only
restricted_tools[tool] if {
tool in ["stripe_charge", "paypal_transfer", "accounting_ledger_write"]
}When policies are declared in a versioned policy repo, they become auditable, and requests can be evaluated against explicit policies in the request path. A prompt instruction and a policy-as-code control look identical until a user tries to override one. The prompt bends, and the policy does not.
For Kubernetes-level guardrails, CEL admission policies enforce pod security at the API level. Note: ValidatingAdmissionPolicy with CEL reached beta in v1.28 (feature-gated) and GA in v1.30. Clusters running v1.24–1.27 cannot use this manifest as shown and will need to use an OPA/Gatekeeper-based equivalent instead:
apiVersion: admissionregistration.k8s.io/v1beta1
kind: ValidatingAdmissionPolicy
metadata:
name: mcp-gateway-policy
spec:
failurePolicy: fail
validations:
- expression: "object.spec.containers[0].securityContext.runAsNonRoot == true"
message: "MCP gateway pods must run as non-root"
- expression: "object.spec.containers[0].securityContext.allowPrivilegeEscalation == false"
message: "MCP gateway pods must not allow privilege escalation"Your pre-launch validation steps:
Verify probe endpoints return HTTP 200 when healthy and 503 when unhealthy.
Confirm credential rotation triggers no service disruption.
Test network policy rules with connectivity tests from inside and outside the namespace.
Validate audit logging captures every tool invocation, including denied calls.
Confirm traces appear in your backend (Jaeger, Datadog).
Drain a node and verify the PDB prevents simultaneous pod evictions.
Run a denied-action test against the policy engine and confirm the deny is logged.
Build vs. buy ROI template
Once the architecture is validated and your platform team has signed off on the manifests, the next step is making the case internally. Use this template to document the decision for your CTO and CFO:
In-house build cost: Engineering hours per integration multiplied by your loaded hourly rate, multiplied by planned integration count.
Ongoing maintenance (annual): Plan on dedicated platform ownership for Kubernetes patching and secrets backend upkeep, not a fraction of one engineer's time.
Self-hosted Enterprise tier: Setup measured in weeks, with connector updates handled by Composio (confirm this scope applies to self-hosted deployments with your account team), plus upstream API drift handled by Composio.
Customer case studies:
11x saved approximately 380 engineering hours across Outlook, Salesforce, and Cal.com integrations while enabling $4.2M in enterprise deals.
Assista AI shipped Gmail, Calendar, GitHub, and Drive integrations live in production within days. Zams shipped Salesforce, HubSpot, Notion, and Slack through the same pattern, per the Zams case study.
Opennote evaluated multiple options and chose Composio on simplicity, per the Opennote case study.
Compliance documentation for the security review
Compliance documentation is the other half of the security review. Composio holds SOC 2 Type II and ISO/IEC 27001:2022 certifications, and Composio's trust center provides pre-filled compliance packs so most enterprise security questionnaires come back within a day rather than a sprint.
Book a call to walk through your self-hosted deployment requirements before the next enterprise security review: talk to the Composio team.
FAQs
What happens when the upstream API changes?
With an in-house build, your team owns the fix and the regression testing. With Composio's managed connector layer, schema updates and OAuth changes are handled centrally, which is the maintenance burden difference between a configuration update and a sprint of engineering work.
How do I handle credential rotation without downtime?
Set a refreshInterval on every ExternalSecret so rotated secrets sync automatically, and verify in staging that pods pick up new values without dropping in-flight requests. Composio's managed OAuth layer handles the full token lifecycle, including refresh, inside its isolated runtime.
Can I run this in an air-gapped cluster?
Yes, with the account-team confirmation noted above. Mirror all container images to an internal registry, vendor your Helm charts, and pre-stage connector definitions, because the cluster cannot pull anything at runtime.
What maintenance burden should I expect after go-live?
Plan for Kubernetes patching, secrets backend upkeep, and policy repo reviews as standing platform work with dedicated ownership. A vendor's self-hosted tier with managed connector updates removes a significant recurring item: upstream API drift.
How does this compare to using a managed service?
A managed service gets you to production in hours with zero infrastructure responsibility, but data transits vendor infrastructure and air-gapped deployment is off the table. Self-hosting costs weeks of setup and ongoing ops in exchange for full data residency and egress control.
Key terms glossary
MCP server: A service that exposes tools to AI agents via the Model Context Protocol. Each server implements a specific set of capabilities (GitHub API access, Gmail operations) and runs in the data plane. Composio owns the tool implementations directly across 1,000+ apps rather than proxying third-party servers, which means action schema changes and authentication updates are handled inside Composio's layer rather than passed through to your team.
Control plane: The governance layer that manages MCP server discovery, authentication, policy enforcement, and audit logging. It never touches tool payloads.
Data plane: The execution layer where tool calls run: gateway proxies, MCP servers, and credential resolution. It never makes authorization decisions.
Policy-as-code: Access rules expressed in a versioned, declarative language (Rego or CEL) and evaluated in the request path before the model is involved.
