> ## Documentation Index
> Fetch the complete documentation index at: https://mcp-server-langgraph.mintlify.site/llms.txt
> Use this file to discover all available pages before exploring further.

# Google GKE Deployment

> Deploy MCP Server with LangGraph on Google Kubernetes Engine (GKE)

### Overview

Google Kubernetes Engine (GKE) is a managed Kubernetes service that provides enterprise-grade features including Autopilot mode, Workload Identity, and tight integration with Google Cloud services.

<Info>
  This guide covers deploying to GKE with production-ready configuration including Workload Identity for secure service authentication, Cloud SQL for PostgreSQL, and Memorystore for Redis.
</Info>

### Prerequisites

* Google Cloud account with billing enabled
* `gcloud` CLI installed and configured
* `kubectl` installed
* Project with required APIs enabled

### Enable Required APIs

```bash theme={null}
## Set project
export PROJECT_ID=your-project-id
gcloud config set project $PROJECT_ID

## Enable APIs
gcloud services enable \
  container.googleapis.com \
  compute.googleapis.com \
  sql-component.googleapis.com \
  redis.googleapis.com \
  secretmanager.googleapis.com \
  cloudkms.googleapis.com \
  servicenetworking.googleapis.com
```

### Create GKE Cluster

#### Autopilot Cluster (Recommended)

GKE Autopilot manages the cluster infrastructure for you:

```bash theme={null}
## Create Autopilot cluster
gcloud container clusters create-auto langgraph-cluster \
  --region=us-central1 \
  --release-channel=regular \
  --enable-cloud-logging \
  --enable-cloud-monitoring \
  --workload-pool=$PROJECT_ID.svc.id.goog

## Get credentials
gcloud container clusters get-credentials langgraph-cluster \
  --region=us-central1
```

#### Standard Cluster

For more control over node configuration:

```bash theme={null}
## Create standard cluster
gcloud container clusters create langgraph-cluster \
  --region=us-central1 \
  --num-nodes=3 \
  --machine-type=n2-standard-4 \
  --disk-size=50 \
  --disk-type=pd-ssd \
  --enable-autoscaling \
  --min-nodes=3 \
  --max-nodes=10 \
  --enable-autorepair \
  --enable-autoupgrade \
  --release-channel=regular \
  --workload-pool=$PROJECT_ID.svc.id.goog \
  --enable-ip-alias \
  --network=default \
  --subnetwork=default \
  --enable-cloud-logging \
  --enable-cloud-monitoring

## Get credentials
gcloud container clusters get-credentials langgraph-cluster \
  --region=us-central1
```

### Setup Workload Identity

Workload Identity allows pods to authenticate as Google Cloud service accounts:

```bash theme={null}
## Create Google Service Account
gcloud iam service-accounts create mcp-server-langgraph \
  --display-name="LangGraph Agent Service Account"

## Grant permissions
gcloud projects add-iam-policy-binding $PROJECT_ID \
  --member="serviceAccount:mcp-server-langgraph@$PROJECT_ID.iam.gserviceaccount.com" \
  --role="roles/secretmanager.secretAccessor"

gcloud projects add-iam-policy-binding $PROJECT_ID \
  --member="serviceAccount:mcp-server-langgraph@$PROJECT_ID.iam.gserviceaccount.com" \
  --role="roles/cloudsql.client"

## Create Kubernetes service account
kubectl create namespace mcp-server-langgraph

kubectl create serviceaccount mcp-server-langgraph \
  --namespace mcp-server-langgraph

## Bind Kubernetes SA to Google SA
gcloud iam service-accounts add-iam-policy-binding \
  mcp-server-langgraph@$PROJECT_ID.iam.gserviceaccount.com \
  --role roles/iam.workloadIdentityUser \
  --member "serviceAccount:$PROJECT_ID.svc.id.goog[mcp-server-langgraph/mcp-server-langgraph]"

kubectl annotate serviceaccount mcp-server-langgraph \
  --namespace mcp-server-langgraph \
  iam.gke.io/gcp-service-account=mcp-server-langgraph@$PROJECT_ID.iam.gserviceaccount.com
```

### Cloud SQL for PostgreSQL

#### Create Cloud SQL Instance

```bash theme={null}
## Create Cloud SQL instance
gcloud sql instances create langgraph-postgres \
  --database-version=POSTGRES_15 \
  --tier=db-custom-2-8192 \
  --region=us-central1 \
  --network=default \
  --enable-google-private-path \
  --backup-start-time=03:00 \
  --backup-location=us \
  --maintenance-window-day=SUN \
  --maintenance-window-hour=4 \
  --database-flags=max_connections=200

## Create databases (Keycloak, OpenFGA, GDPR compliance)
gcloud sql databases create keycloak \
  --instance=langgraph-postgres

gcloud sql databases create openfga \
  --instance=langgraph-postgres

## NEW: GDPR compliance database (ADR-0041)
gcloud sql databases create gdpr \
  --instance=langgraph-postgres

## Create users
gcloud sql users create keycloak \
  --instance=langgraph-postgres \
  --password=$(openssl rand -base64 32)

gcloud sql users create gdpr_user \
  --instance=langgraph-postgres \
  --password=$(openssl rand -base64 32)
```

<Info>
  **Three databases required**: Keycloak (identity), OpenFGA (authorization), and GDPR (compliance data storage per [ADR-0041](/architecture/adr-0041-postgresql-gdpr-storage)).
</Info>

#### Initialize GDPR Schema

After creating the databases, initialize the GDPR schema:

```bash theme={null}
## Get Cloud SQL instance connection name
INSTANCE_CONNECTION_NAME=$(gcloud sql instances describe langgraph-postgres \
  --format="value(connectionName)")

## Connect via Cloud SQL Proxy and apply schema
cloud_sql_proxy -instances=$INSTANCE_CONNECTION_NAME=tcp:5432 &
PROXY_PID=$!

## Apply GDPR schema (5 tables: user_profiles, user_preferences, consent_records, conversations, audit_logs)
PGPASSWORD=your-gdpr-password psql \
  -h 127.0.0.1 \
  -U gdpr_user \
  -d gdpr \
  -f deployments/base/postgres-gdpr-schema.sql

## Stop proxy
kill $PROXY_PID
```

**Schema Details**:

* **user\_profiles**: User profile data (GDPR Article 15, 16, 17)
* **user\_preferences**: User preferences (GDPR Article 16, 17)
* **consent\_records**: Consent audit trail, 7-year retention (GDPR Article 21, Article 7)
* **conversations**: Conversation history, 90-day retention (GDPR Article 15, 20)
* **audit\_logs**: Compliance audit trail, 7-year retention (HIPAA §164.316(b)(2)(i), SOC2 CC6.6)

See [GDPR Storage Configuration](/deployment/gdpr-storage-configuration) for retention policies and backup procedures.

#### Configure Cloud SQL Proxy

**Option 1: Cloud SQL Auth Proxy Sidecar**

```yaml theme={null}
apiVersion: apps/v1
kind: Deployment
metadata:
  name: keycloak
  namespace: mcp-server-langgraph
spec:
  template:
    spec:
      serviceAccountName: mcp-server-langgraph
      containers:
      - name: keycloak
        # IMPORTANT: Use pre-optimized Keycloak image from GHCR
        # Built via .github/workflows/build-keycloak-image.yaml from docker/Dockerfile.keycloak
        # Enables readOnlyRootFilesystem: true (no JIT compilation at runtime)
        image: ghcr.io/vishnu2kmohan/keycloak-optimized:26.4.2
        args:
        - start
        - --optimized
        - --http-enabled=true
        - --http-port=8080
        - --hostname-strict=false
        - --health-enabled=true
        - --metrics-enabled=true
        env:
        - name: KC_DB
          value: postgres
        # KC_HTTP_RELATIVE_PATH for Traefik/Ingress gateway routing
        - name: KC_HTTP_RELATIVE_PATH
          value: /authn
        - name: KC_DB_URL
          value: jdbc:postgresql://127.0.0.1:5432/keycloak
        - name: KC_DB_USERNAME
          valueFrom:
            secretKeyRef:
              name: cloudsql-db-credentials
              key: username
        - name: KC_DB_PASSWORD
          valueFrom:
            secretKeyRef:
              name: cloudsql-db-credentials
              key: password

      # Cloud SQL Proxy sidecar
      - name: cloud-sql-proxy
        image: gcr.io/cloud-sql-connectors/cloud-sql-proxy:2.8.0
        args:
        - "--structured-logs"
        - "--port=5432"
        - "$PROJECT_ID:us-central1:langgraph-postgres"
        securityContext:
          runAsNonRoot: true
        resources:
          requests:
            memory: "128Mi"
            cpu: "100m"
          limits:
            memory: "256Mi"
            cpu: "200m"
```

**Option 2: Private IP (Recommended for Production)**

```bash theme={null}
## Enable private IP for Cloud SQL
gcloud sql instances patch langgraph-postgres \
  --network=projects/$PROJECT_ID/global/networks/default \
  --enable-google-private-path

## Get private IP
CLOUDSQL_PRIVATE_IP=$(gcloud sql instances describe langgraph-postgres \
  --format='get(ipAddresses[0].ipAddress)')

## Use private IP directly
KC_DB_URL=jdbc:postgresql://$CLOUDSQL_PRIVATE_IP:5432/keycloak
```

### Memorystore for Redis

#### Create Redis Instance

```bash theme={null}
## Create Redis instance
gcloud redis instances create langgraph-redis \
  --size=5 \
  --region=us-central1 \
  --tier=standard \
  --redis-version=redis_7_0 \
  --network=default \
  --connect-mode=private-service-access \
  --enable-auth

## Get Redis host and auth string
REDIS_HOST=$(gcloud redis instances describe langgraph-redis \
  --region=us-central1 \
  --format='get(host)')

REDIS_AUTH=$(gcloud redis instances get-auth-string langgraph-redis \
  --region=us-central1)

## Create secret
kubectl create secret generic redis-credentials \
  --namespace=mcp-server-langgraph \
  --from-literal=host=$REDIS_HOST \
  --from-literal=password=$REDIS_AUTH
```

#### Configure Application

```yaml theme={null}
apiVersion: apps/v1
kind: Deployment
metadata:
  name: mcp-server-langgraph
spec:
  template:
    spec:
      containers:
      - name: agent
        env:
        - name: REDIS_URL
          value: redis://:$(REDIS_PASSWORD)@$(REDIS_HOST):6379
        - name: REDIS_HOST
          valueFrom:
            secretKeyRef:
              name: redis-credentials
              key: host
        - name: REDIS_PASSWORD
          valueFrom:
            secretKeyRef:
              name: redis-credentials
              key: password
```

### Secret Manager Integration

#### Store Secrets in Secret Manager

```bash theme={null}
## Create secrets
echo -n "sk-ant-your-key" | gcloud secrets create anthropic-api-key \
  --data-file=- \
  --replication-policy=automatic

echo -n "your-jwt-secret" | gcloud secrets create jwt-secret \
  --data-file=- \
  --replication-policy=automatic

## Grant access to service account
gcloud secrets add-iam-policy-binding anthropic-api-key \
  --member="serviceAccount:mcp-server-langgraph@$PROJECT_ID.iam.gserviceaccount.com" \
  --role="roles/secretmanager.secretAccessor"
```

#### Use External Secrets Operator

```bash theme={null}
## Install External Secrets Operator
helm repo add external-secrets https://charts.external-secrets.io
helm repo update

helm install external-secrets \
  external-secrets/external-secrets \
  --namespace external-secrets-system \
  --create-namespace

## Create SecretStore
cat <<'EOF' | kubectl apply -f -
apiVersion: external-secrets.io/v1beta1
kind: SecretStore
metadata:
  name: gcpsm-secret-store
  namespace: mcp-server-langgraph
spec:
  provider:
    gcpsm:
      projectID: "$PROJECT_ID"
      auth:
        workloadIdentity:
          clusterLocation: us-central1
          clusterName: langgraph-cluster
          serviceAccountRef:
            name: mcp-server-langgraph
EOF

## Create ExternalSecret
cat <<'EOF' | kubectl apply -f -
apiVersion: external-secrets.io/v1beta1
kind: ExternalSecret
metadata:
  name: langgraph-secrets
  namespace: mcp-server-langgraph
spec:
  refreshInterval: 1h
  secretStoreRef:
    name: gcpsm-secret-store
    kind: SecretStore
  target:
    name: mcp-server-langgraph-secrets
    creationPolicy: Owner
  data:
  - secretKey: ANTHROPIC_API_KEY
    remoteRef:
      key: anthropic-api-key
  - secretKey: JWT_SECRET
    remoteRef:
      key: jwt-secret
EOF
```

### Google Container Registry (GCR)

#### Build and Push Images

```bash theme={null}
## Configure Docker for GCR
gcloud auth configure-docker

## Build image
docker build -t gcr.io/$PROJECT_ID/mcp-server-langgraph:latest .

## Push to GCR
docker push gcr.io/$PROJECT_ID/mcp-server-langgraph:latest

## Use in Kubernetes
cat << 'EOF' | kubectl apply -f -
apiVersion: apps/v1
kind: Deployment
metadata:
  name: mcp-server-langgraph
  namespace: mcp-server-langgraph
spec:
  template:
    spec:
      containers:
      - name: agent
        image: gcr.io/$PROJECT_ID/mcp-server-langgraph:latest
        imagePullPolicy: Always
EOF
```

#### Artifact Registry (Recommended)

```bash theme={null}
## Create Artifact Registry repository
gcloud artifacts repositories create langgraph \
  --repository-format=docker \
  --location=us-central1 \
  --description="LangGraph Agent images"

## Configure Docker
gcloud auth configure-docker us-central1-docker.pkg.dev

## Build and push
docker build -t us-central1-docker.pkg.dev/$PROJECT_ID/langgraph/agent:latest .
docker push us-central1-docker.pkg.dev/$PROJECT_ID/langgraph/agent:latest
```

### Load Balancer and Ingress

#### Install NGINX Ingress Controller

```bash theme={null}
## Install NGINX Ingress
helm repo add ingress-nginx https://kubernetes.github.io/ingress-nginx
helm repo update

helm install ingress-nginx ingress-nginx/ingress-nginx \
  --namespace ingress-nginx \
  --create-namespace \
  --set controller.service.type=LoadBalancer
```

#### Configure Ingress

```yaml theme={null}
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: langgraph-ingress
  namespace: mcp-server-langgraph
  annotations:
    kubernetes.io/ingress.class: nginx
    cert-manager.io/cluster-issuer: letsencrypt-prod
    nginx.ingress.kubernetes.io/ssl-redirect: "true"
    nginx.ingress.kubernetes.io/force-ssl-redirect: "true"
spec:
  tls:
  - hosts:
    - api.yourdomain.com
    secretName: langgraph-tls
  rules:
  - host: api.yourdomain.com
    http:
      paths:
      - path: /
        pathType: Prefix
        backend:
          service:
            name: mcp-server-langgraph
            port:
              number: 8000
```

#### Google Cloud Load Balancer

For GCP-native load balancing:

```yaml theme={null}
apiVersion: v1
kind: Service
metadata:
  name: mcp-server-langgraph
  namespace: mcp-server-langgraph
  annotations:
    cloud.google.com/neg: '{"ingress": true}'
    cloud.google.com/backend-config: '{"default": "langgraph-backend-config"}'
spec:
  type: LoadBalancer
  loadBalancerIP: 34.xxx.xxx.xxx  # Reserve static IP
  ports:
  - port: 80
    targetPort: 8000
    protocol: TCP
  selector:
    app: mcp-server-langgraph
---
apiVersion: cloud.google.com/v1
kind: BackendConfig
metadata:
  name: langgraph-backend-config
  namespace: mcp-server-langgraph
spec:
  healthCheck:
    checkIntervalSec: 15
    port: 8000
    type: HTTP
    requestPath: /health/ready
  timeoutSec: 30
  connectionDraining:
    drainingTimeoutSec: 60
  cdn:
    enabled: false
```

### Monitoring with Cloud Operations

#### Enable Google Cloud Monitoring

```bash theme={null}
## Install Google Cloud Ops agent
kubectl apply -f https://raw.githubusercontent.com/GoogleCloudPlatform/prometheus-engine/v0.8.0/manifests/setup.yaml

kubectl apply -f https://raw.githubusercontent.com/GoogleCloudPlatform/prometheus-engine/v0.8.0/manifests/operator.yaml
```

#### Configure Pod Monitoring

```yaml theme={null}
apiVersion: monitoring.googleapis.com/v1
kind: PodMonitoring
metadata:
  name: mcp-server-langgraph
  namespace: mcp-server-langgraph
spec:
  selector:
    matchLabels:
      app: mcp-server-langgraph
  endpoints:
  - port: metrics
    interval: 30s
```

#### Cloud Logging

Configure structured logging to Cloud Logging:

```python theme={null}
## Install dependencies
# uv add google-cloud-logging

## Configure logging
import google.cloud.logging
from google.cloud.logging.handlers import CloudLoggingHandler

client = google.cloud.logging.Client()
handler = CloudLoggingHandler(client)

## Use with structlog
import structlog

structlog.configure(
    processors=[
        structlog.processors.JSONRenderer()
    ],
    wrapper_class=structlog.stdlib.BoundLogger,
    logger_factory=structlog.stdlib.LoggerFactory(),
)
```

### Auto-Scaling

#### Horizontal Pod Autoscaler

```yaml theme={null}
apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata:
  name: mcp-server-langgraph
  namespace: mcp-server-langgraph
spec:
  scaleTargetRef:
    apiVersion: apps/v1
    kind: Deployment
    name: mcp-server-langgraph
  minReplicas: 3
  maxReplicas: 50
  metrics:
  - type: Resource
    resource:
      name: cpu
      target:
        type: Utilization
        averageUtilization: 70
  - type: Resource
    resource:
      name: memory
      target:
        type: Utilization
        averageUtilization: 80
  behavior:
    scaleDown:
      stabilizationWindowSeconds: 300
      policies:
      - type: Percent
        value: 50
        periodSeconds: 60
    scaleUp:
      stabilizationWindowSeconds: 0
      policies:
      - type: Percent
        value: 100
        periodSeconds: 30
      - type: Pods
        value: 5
        periodSeconds: 60
      selectPolicy: Max
```

#### Cluster Autoscaler

GKE Autopilot handles node scaling automatically. For Standard clusters:

```bash theme={null}
## Enable cluster autoscaling
gcloud container clusters update langgraph-cluster \
  --enable-autoscaling \
  --min-nodes=3 \
  --max-nodes=20 \
  --region=us-central1
```

### Complete Deployment

```bash theme={null}
## Clone repository
git clone https://github.com/vishnu2kmohan/mcp-server-langgraph
cd mcp-server-langgraph

## Build and push image
docker build -t us-central1-docker.pkg.dev/$PROJECT_ID/langgraph/agent:v1.0.0 .
docker push us-central1-docker.pkg.dev/$PROJECT_ID/langgraph/agent:v1.0.0

## Create namespace
kubectl create namespace mcp-server-langgraph

## Deploy with Helm
helm install mcp-server-langgraph ./helm/mcp-server-langgraph \
  --namespace mcp-server-langgraph \
  --set image.repository=us-central1-docker.pkg.dev/$PROJECT_ID/langgraph/agent \
  --set image.tag=v1.0.0 \
  --set workloadIdentity.enabled=true \
  --set workloadIdentity.serviceAccount=mcp-server-langgraph@$PROJECT_ID.iam.gserviceaccount.com \
  --set cloudsql.instance=$PROJECT_ID:us-central1:langgraph-postgres \
  --set redis.host=$REDIS_HOST

## Verify deployment
kubectl get pods -n mcp-server-langgraph
kubectl get svc -n mcp-server-langgraph
kubectl get ingress -n mcp-server-langgraph
```

### Cost Optimization

<AccordionGroup>
  <Accordion title="Use Autopilot Mode" icon="wand-magic-sparkles">
    GKE Autopilot optimizes resource usage:

    * Pay only for pod resources
    * Automatic resource optimization
    * No node management overhead
    * Built-in security best practices
  </Accordion>

  <Accordion title="Enable Preemptible Nodes" icon="dollar-sign">
    For non-production workloads:

    ```bash theme={null}
    gcloud container node-pools create preemptible-pool \
      --cluster=langgraph-cluster \
      --region=us-central1 \
      --preemptible \
      --num-nodes=3 \
      --machine-type=n2-standard-4
    ```

    Use node affinity to schedule on preemptible nodes:

    ```yaml theme={null}
    affinity:
      nodeAffinity:
        preferredDuringSchedulingIgnoredDuringExecution:
        - weight: 100
          preference:
            matchExpressions:
            - key: cloud.google.com/gke-preemptible
              operator: In
              values:
              - "true"
    ```
  </Accordion>

  <Accordion title="Right-Size Resources" icon="gauge">
    Use GKE usage metering to analyze resource usage:

    ```bash theme={null}
    # Enable usage metering
    gcloud container clusters update langgraph-cluster \
      --resource-usage-bigquery-dataset=cluster_usage \
      --enable-network-egress-metering \
      --enable-resource-consumption-metering \
      --region=us-central1
    ```
  </Accordion>

  <Accordion title="Use Committed Use Discounts" icon="percent">
    For production workloads:

    * 1-year commitment: 37% discount
    * 3-year commitment: 55% discount

    ```bash theme={null}
    gcloud compute commitments create \
      --plan=12-month \
      --resources=vcpu=100,memory=400GB \
      --region=us-central1
    ```
  </Accordion>
</AccordionGroup>

### Security Best Practices

<AccordionGroup>
  <Accordion title="Enable Shielded Nodes" icon="shield">
    ```bash theme={null}
    gcloud container clusters update langgraph-cluster \
      --enable-shielded-nodes \
      --region=us-central1
    ```
  </Accordion>

  <Accordion title="Use Binary Authorization" icon="shield-check">
    Ensure only trusted images are deployed:

    ```bash theme={null}
    # Enable Binary Authorization
    gcloud container clusters update langgraph-cluster \
      --enable-binauthz \
      --region=us-central1
    ```
  </Accordion>

  <Accordion title="Network Policies" icon="network-wired">
    ```yaml theme={null}
    apiVersion: networking.k8s.io/v1
    kind: NetworkPolicy
    metadata:
      name: allow-agent-egress
      namespace: mcp-server-langgraph
    spec:
      podSelector:
        matchLabels:
          app: mcp-server-langgraph
      policyTypes:
      - Egress
      egress:
      - to:
        - podSelector:
            matchLabels:
              app: keycloak
        ports:
        - protocol: TCP
          port: 8080
      - to:
        - podSelector:
            matchLabels:
              app: openfga
        ports:
        - protocol: TCP
          port: 8080
    ```
  </Accordion>
</AccordionGroup>

### Troubleshooting

<AccordionGroup>
  <Accordion title="Cloud SQL connection fails">
    **Check connectivity**:

    ```bash theme={null}
    # Test Cloud SQL Proxy
    kubectl run -it --rm debug \
      --image=gcr.io/cloud-sql-connectors/cloud-sql-proxy:latest \
      --restart=Never \
      -- $PROJECT_ID:us-central1:langgraph-postgres
    ```
  </Accordion>

  <Accordion title="Workload Identity not working">
    **Verify setup**:

    ```bash theme={null}
    # Check annotation
    kubectl describe sa mcp-server-langgraph -n mcp-server-langgraph

    # Test from pod
    kubectl run -it --rm test \
      --image=google/cloud-sdk:slim \
      --serviceaccount=mcp-server-langgraph \
      --namespace=mcp-server-langgraph \
      -- gcloud auth list
    ```
  </Accordion>
</AccordionGroup>

### Next Steps

<CardGroup cols={2}>
  <Card title="EKS Deployment" icon="aws" href="/deployment/kubernetes/eks">
    Deploy to AWS EKS
  </Card>

  <Card title="AKS Deployment" icon="microsoft" href="/deployment/kubernetes/aks">
    Deploy to Azure AKS
  </Card>

  <Card title="Monitoring" icon="chart-line" href="/deployment/monitoring">
    Setup monitoring
  </Card>

  <Card title="Disaster Recovery" icon="life-ring" href="/deployment/disaster-recovery">
    Backup and recovery
  </Card>
</CardGroup>

***

<Check>
  **GKE Deployment Ready**: Production-grade deployment on Google Kubernetes Engine!
</Check>
