PythonDeploymentCloud Platforms

Google Cloud

Run the server on Cloud Run or GKE

Google Cloud offers two ways to run the container. Cloud Run is serverless: it scales with traffic and you pay per request. GKE is managed Kubernetes, for when you already run a cluster.

Every platform needs the same settings from your server: listen on all interfaces (HOST=0.0.0.0), use the port the platform expects (PORT), run over HTTP (MCP_TRANSPORT_TYPE=http), and point the health check at /mcp/health. Docker explains each one.

Cloud Run

Cloud Build builds the image from your Dockerfile, and gcloud run deploy starts it with your settings and secrets.

# Enable APIs
gcloud services enable run.googleapis.com cloudbuild.googleapis.com

# Build with Cloud Build
gcloud builds submit --tag gcr.io/PROJECT_ID/my-mcp-server

# Deploy
gcloud run deploy my-mcp-server \
  --image gcr.io/PROJECT_ID/my-mcp-server \
  --platform managed \
  --region us-central1 \
  --allow-unauthenticated \
  --set-env-vars HOST=0.0.0.0,MCP_TRANSPORT_TYPE=http \
  --set-secrets JWT_SECRET=jwt-secret:latest

Cloud Run injects PORT, and nitrostack-py start reads it from the environment. Remove --allow-unauthenticated and use Cloud Run IAM if the server is private. Clients connect to https://<service-url>/mcp.

GKE

This manifest runs two replicas behind a load balancer, reads JWT_SECRET from a Kubernetes secret, and uses /mcp/health as the readiness probe.

# deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: mcp-server
spec:
  replicas: 2
  selector:
    matchLabels:
      app: mcp-server
  template:
    metadata:
      labels:
        app: mcp-server
    spec:
      containers:
        - name: mcp-server
          image: gcr.io/PROJECT_ID/my-mcp-server:latest
          ports:
            - containerPort: 3000
          env:
            - name: HOST
              value: "0.0.0.0"
            - name: MCP_TRANSPORT_TYPE
              value: "http"
            - name: JWT_SECRET
              valueFrom:
                secretKeyRef:
                  name: mcp-secrets
                  key: jwt-secret
          readinessProbe:
            httpGet:
              path: /mcp/health
              port: 3000
---
apiVersion: v1
kind: Service
metadata:
  name: mcp-server
spec:
  type: LoadBalancer
  selector:
    app: mcp-server
  ports:
    - port: 80
      targetPort: 3000

Apply it to your cluster:

kubectl apply -f deployment.yaml

Secrets

Prefer injecting secrets as environment variables (--set-secrets on Cloud Run, secretKeyRef on GKE). To read one from code instead, use Secret Manager:

from google.cloud import secretmanager

def get_secret(project_id: str, name: str) -> str:
    client = secretmanager.SecretManagerServiceClient()
    path = f"projects/{project_id}/secrets/{name}/versions/latest"
    return client.access_secret_version(name=path).payload.data.decode("utf-8")

Logs

Cloud Run sends container stdout and stderr to Cloud Logging automatically. Print JSON with a severity field to get structured entries:

import json
import sys

def log_entry(message: str, severity: str = "INFO") -> None:
    print(json.dumps({"severity": severity, "message": message}), file=sys.stderr)

Next steps

On this page