This guide installs MCPG on Kubernetes for production use: the operator, an HA gateway deployment, and shared state via NATS (or Redis). Target audience: SREs and platform teams.
Prerequisites
- Kubernetes 1.26 or newer
- Helm 3.x
- A namespace to deploy into (we'll use
mcpg) - Either NATS or Redis available in-cluster (we'll deploy NATS as a chart dependency)
1. Install the operator
The operator watches MCPGGateway, MCPGPlugin, MCPGPluginSet, and MCPGRevocationList
CRDs cluster-wide.
kubectl create namespace mcpg
helm repo add mcpg https://charts.mcpg.dev
helm repo update
helm install mcpg-operator mcpg/mcpg-operator -n mcpg \
--set rbac.create=true \
--set webhook.enabled=true
Verify:
kubectl -n mcpg get pods
# mcpg-operator-7d8c4b9b8-xj2pq 1/1 Running
2. Install the control plane
The control plane is optional for single-instance deployments but required for fleets.
helm install mcpg-cp mcpg/mcpg-cp -n mcpg \
--set replicaCount=3 \
--set postgres.enabled=true \
--set postgres.auth.password=$(openssl rand -hex 16) \
--set ingress.enabled=true \
--set ingress.host=mcpg-cp.example.com
This deploys 3 cp-server replicas with Postgres for state, an Ingress at the configured
host, and the embedded dashboard. Login uses OIDC (configure in oidc.* values) or local
admin credentials.
3. Install a gateway fleet
helm install gw mcpg/mcpg -n mcpg \
--set replicaCount=3 \
--set autoscaling.enabled=true \
--set autoscaling.minReplicas=3 \
--set autoscaling.maxReplicas=10 \
--set podDisruptionBudget.enabled=true \
--set nats.enabled=true \
--set controlPlane.url=https://mcpg-cp.example.com \
--set controlPlane.enrollmentToken=$(mcpg-ctl token --once)
Each gateway replica registers itself with the control plane on startup using the single-use enrollment token. The CP hands back an instance JWT and starts pushing config.
4. Verify the fleet
In the dashboard:
- Navigate to Instances.
- You should see 3 instances in
Activestate. - Click one to drill into per-instance status, plugins, and tool calls.
CLI verification:
mcpg-ctl gateway status
# 3 instances · 0 quarantined · 0 dirty
5. Bind a plugin set
Create the plugin set as a CRD:
apiVersion: mcpg.dev/v1
kind: MCPGPluginSet
metadata:
name: production
namespace: mcpg
spec:
plugins:
- id: identity.oidc
version: "1.0.0"
- id: policy.cedar
version: "1.0.0"
config:
bundle: ./policies/
- id: reliability.rate-limit
version: "1.0.0"
config:
default_rps: 100
Apply:
kubectl apply -f plugin-set.yaml
Bind to the gateway:
kubectl patch mcpggateway gw -n mcpg --type=merge \
-p '{"spec":{"pluginSetRef":{"name":"production"}}}'
The operator reconciles, the CP pushes a ConfigUpdate, and each gateway replica reloads
without dropping connections.
6. Connect upstream tools
The same bindings: config from the quickstart guide works
unchanged. Mount it as a ConfigMap and reference it in the MCPGGateway spec.
Cluster state options
| Backend | Pick when |
|---|---|
NATS JetStream (nats.enabled=true) | You don't already run Redis. JetStream gives KV + pub/sub + leases in one component. |
Redis (redis.enabled=true) | You already run Redis. Lower operational overhead. |
| Built-in single-node | Single-instance only. Don't use this for HA. |
See the cluster backends article for the trade-offs.
What's next
- Observability deep-dive — Prometheus, OTLP, audit
- Identity setup — wire OIDC / SAML / SPIFFE
- Plugin authoring — write your own plugin