Deploy MCP Server to AWS/GCP/Azure

Deploy your MCP server to major cloud platforms with guides for AWS ECS, Google Cloud Run, and Azure Container Apps.


title: "Deploy MCP Server to AWS/GCP/Azure" description: "Deploy your MCP server to major cloud platforms with guides for AWS ECS, Google Cloud Run, and Azure Container Apps." order: 12 level: "advanced" duration: "40 min" keywords:

  • MCP cloud deployment
  • MCP AWS
  • MCP GCP
  • MCP Azure
  • deploy MCP server cloud date: "2026-04-01"

Quick Summary

Deploy your MCP server to AWS, Google Cloud, or Azure. This guide covers container-based deployments with SSE transport, including ECS Fargate, Cloud Run, and Azure Container Apps. Includes production hardening, environment configuration, and monitoring setup.

Prerequisites

Before deploying to the cloud, make sure you have:

  • A working MCP server with SSE transport
  • A Docker image of your server
  • CLI tools for your cloud provider (aws, gcloud, or az)
  • Basic familiarity with your chosen cloud platform
SSE Transport Required

Cloud deployments require SSE transport since there is no way to use stdio pipes over the network. Make sure your server supports SSE before deploying. See the SSE transport tutorial for setup instructions.

Architecture Overview

Cloud MCP Architecture

A typical cloud MCP deployment consists of a containerized MCP server behind an HTTPS load balancer, with secrets stored in a managed secrets service and logs flowing to a centralized logging system. The AI client connects to the load balancer's URL over SSE.

3 cloudsAWS, GCP, and Azure covered

AWS: ECS Fargate

Push Image to ECR

# Create ECR repository
aws ecr create-repository --repository-name mcp-server

# Login, tag, and push
aws ecr get-login-password --region us-east-1 | docker login --username AWS --password-stdin YOUR_ACCOUNT.dkr.ecr.us-east-1.amazonaws.com

docker tag my-mcp-server:latest YOUR_ACCOUNT.dkr.ecr.us-east-1.amazonaws.com/mcp-server:latest
docker push YOUR_ACCOUNT.dkr.ecr.us-east-1.amazonaws.com/mcp-server:latest

Task Definition

Create ecs-task-definition.json:

{
  "family": "mcp-server",
  "networkMode": "awsvpc",
  "requiresCompatibilities": ["FARGATE"],
  "cpu": "256",
  "memory": "512",
  "executionRoleArn": "arn:aws:iam::YOUR_ACCOUNT:role/ecsTaskExecutionRole",
  "containerDefinitions": [
    {
      "name": "mcp-server",
      "image": "YOUR_ACCOUNT.dkr.ecr.us-east-1.amazonaws.com/mcp-server:latest",
      "portMappings": [
        {
          "containerPort": 3001,
          "protocol": "tcp"
        }
      ],
      "environment": [
        { "name": "PORT", "value": "3001" },
        { "name": "NODE_ENV", "value": "production" }
      ],
      "secrets": [
        {
          "name": "API_KEY",
          "valueFrom": "arn:aws:secretsmanager:us-east-1:YOUR_ACCOUNT:secret:mcp-api-key"
        }
      ],
      "logConfiguration": {
        "logDriver": "awslogs",
        "options": {
          "awslogs-group": "/ecs/mcp-server",
          "awslogs-region": "us-east-1",
          "awslogs-stream-prefix": "ecs"
        }
      },
      "healthCheck": {
        "command": ["CMD-SHELL", "wget --no-verbose --tries=1 --spider http://localhost:3001/health || exit 1"],
        "interval": 30,
        "timeout": 10,
        "retries": 3,
        "startPeriod": 15
      }
    }
  ]
}

Deploy the Service

# Register task definition
aws ecs register-task-definition --cli-input-json file://ecs-task-definition.json

# Create service with ALB
aws ecs create-service \
  --cluster default \
  --service-name mcp-server \
  --task-definition mcp-server \
  --desired-count 1 \
  --launch-type FARGATE \
  --network-configuration "awsvpcConfiguration={subnets=[subnet-xxx],securityGroups=[sg-xxx],assignPublicIp=ENABLED}" \
  --load-balancers "targetGroupArn=arn:aws:elasticloadbalancing:...,containerName=mcp-server,containerPort=3001"
Use ALB for SSE

AWS Application Load Balancer (ALB) supports SSE connections with long timeouts. Set the idle timeout to 3600 seconds to prevent premature connection drops. Network Load Balancers (NLB) also work well for SSE.

Google Cloud: Cloud Run

Cloud Run is the simplest option -- it handles scaling, HTTPS, and container orchestration automatically.

Push Image to Artifact Registry

# Create repository
gcloud artifacts repositories create mcp-servers \
  --repository-format=docker \
  --location=us-central1

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

# Tag and push
docker tag my-mcp-server us-central1-docker.pkg.dev/YOUR_PROJECT/mcp-servers/mcp-server:latest
docker push us-central1-docker.pkg.dev/YOUR_PROJECT/mcp-servers/mcp-server:latest

Deploy to Cloud Run

gcloud run deploy mcp-server \
  --image us-central1-docker.pkg.dev/YOUR_PROJECT/mcp-servers/mcp-server:latest \
  --port 3001 \
  --region us-central1 \
  --allow-unauthenticated \
  --set-env-vars "NODE_ENV=production" \
  --set-secrets "API_KEY=mcp-api-key:latest" \
  --min-instances 1 \
  --max-instances 5 \
  --timeout 3600 \
  --session-affinity
Session Affinity

Enable session affinity (sticky sessions) on Cloud Run for SSE transport. This ensures that all requests from a single client go to the same container instance, which is necessary for stateful SSE connections.

Cloud Run Configuration for SSE

Key settings for SSE on Cloud Run:

# Increase request timeout for long-lived SSE connections
gcloud run services update mcp-server \
  --timeout 3600 \
  --session-affinity \
  --min-instances 1

Azure: Container Apps

Push Image to Azure Container Registry

# Create ACR
az acr create --name mcpservers --resource-group mygroup --sku Basic

# Login and push
az acr login --name mcpservers
docker tag my-mcp-server mcpservers.azurecr.io/mcp-server:latest
docker push mcpservers.azurecr.io/mcp-server:latest

Deploy to Container Apps

# Create Container Apps environment
az containerapp env create \
  --name mcp-env \
  --resource-group mygroup \
  --location eastus

# Deploy
az containerapp create \
  --name mcp-server \
  --resource-group mygroup \
  --environment mcp-env \
  --image mcpservers.azurecr.io/mcp-server:latest \
  --target-port 3001 \
  --ingress external \
  --min-replicas 1 \
  --max-replicas 5 \
  --env-vars "NODE_ENV=production" "PORT=3001" \
  --secrets "api-key=YOUR_API_KEY" \
  --secret-env-vars "API_KEY=api-key"

Cloud Comparison

FeatureAWS ECSGCP Cloud RunAzure Container Apps
Setup complexityHighLowMedium
Auto-scalingConfigurableAutomaticConfigurable
SSE supportVia ALBNativeNative
Minimum cost~$15/monthPay per request~$10/month
Cold startNo (always running)Yes (can set min=1)Yes (can set min=1)
Best forComplex infraSimple deploysAzure ecosystem

Production Hardening

HTTPS and Custom Domain

All three platforms provide HTTPS automatically. For custom domains:

# GCP Cloud Run
gcloud run domain-mappings create \
  --service mcp-server \
  --domain mcp.yourdomain.com \
  --region us-central1

# AWS (via ALB + ACM certificate)
aws acm request-certificate --domain-name mcp.yourdomain.com

# Azure
az containerapp hostname add \
  --name mcp-server \
  --resource-group mygroup \
  --hostname mcp.yourdomain.com

Authentication for Production

Add token-based authentication to your server:

app.use("/sse", (req, res, next) => {
  const token = req.query.token || req.headers.authorization?.split(" ")[1];
  if (token !== process.env.MCP_AUTH_TOKEN) {
    res.status(401).json({ error: "Unauthorized" });
    return;
  }
  next();
});

Monitoring and Alerts

Set up basic monitoring on any cloud:

# AWS CloudWatch alarm
aws cloudwatch put-metric-alarm \
  --alarm-name mcp-server-errors \
  --metric-name 5XXError \
  --namespace AWS/ApplicationELB \
  --threshold 5 \
  --comparison-operator GreaterThanThreshold \
  --evaluation-periods 2 \
  --period 300

# GCP alerts are configured in the Console or via Terraform

# Azure Monitor
az monitor metrics alert create \
  --name mcp-server-health \
  --resource-group mygroup \
  --scopes "/subscriptions/.../mcp-server" \
  --condition "avg ResponseTime > 5000" \
  --action-group default
Always Set Min Instances

For SSE servers, always set minimum instances to at least 1. Cold starts break SSE connections, and your clients would need to reconnect. The small cost of keeping one instance warm is worth the reliability.

Connecting from Claude Desktop

Once deployed, update your Claude Desktop config:

{
  "mcpServers": {
    "cloud-server": {
      "url": "https://mcp.yourdomain.com/sse?token=your-auth-token",
      "transport": "sse"
    }
  }
}

Frequently Asked Questions