Google Cloud
Run your MCP server on Cloud Run or Google Kubernetes Engine
Google Cloud offers two good homes for a container. Cloud Run is the simpler one: you give it an image, and it serves it over HTTPS, scales the number of instances with traffic, and bills only while requests are running. GKE is managed Kubernetes, for teams that already run their services on a cluster.
Either way, clients connect to https://<your-service-url>/mcp.
Before you start
Build the image from Deploy with Docker and test it locally. You also need the gcloud CLI, signed in, with a project selected.
Deploy
Choose Cloud Run for a managed service, or GKE if you already run Kubernetes.
Build the image in the cloud
Cloud Build builds the image from your Dockerfile and stores it in Artifact Registry, so you don't need Docker running locally.
gcloud services enable run.googleapis.com cloudbuild.googleapis.com artifactregistry.googleapis.com
gcloud artifacts repositories create mcp --repository-format=docker --location=us-central1
gcloud builds submit --tag us-central1-docker.pkg.dev/PROJECT_ID/mcp/mcp-serverDeploy the service
Deploy the image as a Cloud Run service, with the environment variables the server needs:
gcloud run deploy mcp-server \
--image us-central1-docker.pkg.dev/PROJECT_ID/mcp/mcp-server \
--region us-central1 \
--port 3000 \
--memory 512Mi \
--set-env-vars NODE_ENV=production,HOST=0.0.0.0,MCP_TRANSPORT_TYPE=http \
--allow-unauthenticatedThe command prints the service URL. Your MCP endpoint is that URL followed by /mcp.
--allow-unauthenticated lets any client reach the service, which is what public MCP clients need. Protect your tools with authentication in the server itself. If only your own services should call it, leave the flag out and Cloud Run requires a Google identity token on every request.
Build and push the image as in the Cloud Run tab, then apply this manifest with kubectl apply -f deployment.yaml. It runs three copies of the server, checks their health, and exposes them through a load balancer on port 80.
apiVersion: apps/v1
kind: Deployment
metadata:
name: mcp-server
spec:
replicas: 3
selector:
matchLabels:
app: mcp-server
template:
metadata:
labels:
app: mcp-server
spec:
containers:
- name: mcp-server
image: us-central1-docker.pkg.dev/PROJECT_ID/mcp/mcp-server
ports:
- containerPort: 3000
env:
- name: NODE_ENV
value: "production"
- name: HOST
value: "0.0.0.0"
- name: MCP_TRANSPORT_TYPE
value: "http"
readinessProbe:
httpGet:
path: /mcp/health
port: 3000
livenessProbe:
httpGet:
path: /mcp/health
port: 3000
initialDelaySeconds: 10
resources:
requests:
memory: "512Mi"
cpu: "250m"
limits:
memory: "1Gi"
cpu: "500m"
---
apiVersion: v1
kind: Service
metadata:
name: mcp-server
spec:
type: LoadBalancer
ports:
- port: 80
targetPort: 3000
selector:
app: mcp-serverSecrets
Store credentials in Secret Manager and let the platform inject them as environment variables at startup. Your code keeps reading process.env.
printf "your-secret-value" | gcloud secrets create jwt-secret --data-file=-
gcloud run services update mcp-server --region us-central1 \
--set-secrets JWT_SECRET=jwt-secret:latestThe Cloud Run service account needs the Secret Manager Secret Accessor role on the secret. On GKE, create a Kubernetes Secret, or use the Secret Manager add-on, and reference it with valueFrom.secretKeyRef in the manifest.
Health checks
The Kubernetes manifest above uses the built-in /mcp/health endpoint for both probes: a pod receives traffic only after the readiness probe passes, and is restarted if the liveness probe fails. On Cloud Run, you can add the same path as a startup or liveness probe in the service's container settings.
Logs
Both Cloud Run and GKE send everything your server writes to standard output to Cloud Logging automatically. Read recent entries for a Cloud Run service from the terminal:
gcloud run services logs read mcp-server --region us-central1