PythonDeploymentCloud Platforms

AWS

Run the server on ECS Fargate or Lambda, with Secrets Manager and CloudWatch

On AWS, the usual home for an MCP server is ECS on Fargate: AWS runs your container image behind a load balancer and restarts it when the health check fails. Clients connect to https://<your-load-balancer>/mcp.

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.

Deploy to ECS

Create an ECR repository

ECR stores the container image that ECS pulls.

aws ecr create-repository --repository-name my-mcp-server

Build and push the image

Sign Docker in to ECR, then build and push. The image comes from the Dockerfile in Docker.

aws ecr get-login-password --region us-east-1 | \
  docker login --username AWS --password-stdin <account-id>.dkr.ecr.us-east-1.amazonaws.com

docker build -t my-mcp-server .
docker tag my-mcp-server:latest <account-id>.dkr.ecr.us-east-1.amazonaws.com/my-mcp-server:latest
docker push <account-id>.dkr.ecr.us-east-1.amazonaws.com/my-mcp-server:latest

Register a task definition

The task definition sets the image, the port, the environment, and which secrets to inject from Secrets Manager.

{
  "family": "mcp-server",
  "networkMode": "awsvpc",
  "requiresCompatibilities": ["FARGATE"],
  "cpu": "256",
  "memory": "512",
  "containerDefinitions": [
    {
      "name": "mcp-server",
      "image": "<account-id>.dkr.ecr.us-east-1.amazonaws.com/my-mcp-server:latest",
      "portMappings": [{ "containerPort": 3000 }],
      "environment": [
        { "name": "HOST", "value": "0.0.0.0" },
        { "name": "MCP_TRANSPORT_TYPE", "value": "http" },
        { "name": "PORT", "value": "3000" }
      ],
      "secrets": [
        {
          "name": "JWT_SECRET",
          "valueFrom": "arn:aws:secretsmanager:us-east-1:<account-id>:secret:mcp/jwt-secret"
        }
      ]
    }
  ]
}

Create the service

The service keeps two copies of the task running.

aws ecs create-service \
  --cluster mcp-cluster \
  --service-name mcp-server \
  --task-definition mcp-server \
  --desired-count 2 \
  --launch-type FARGATE

Point the load balancer health check at /mcp/health.

Lambda

Lambda works for stateless HTTP use, but MCP sessions are stateful, so prefer ECS for long-lived sessions. If you still want Lambda, deploy a container image with the Lambda Web Adapter so the same nitrostack-py start process serves requests:

FROM public.ecr.aws/docker/library/python:3.12-slim
COPY --from=public.ecr.aws/awsguru/aws-lambda-adapter:0.8.4 /lambda-adapter /opt/extensions/lambda-adapter
WORKDIR /app
COPY . .
RUN pip install --no-cache-dir nitrostack -r requirements.txt
ENV HOST=0.0.0.0 PORT=8080 MCP_TRANSPORT_TYPE=http
CMD ["nitrostack-py", "start", "--port", "8080"]

Secrets

The task definition above injects JWT_SECRET from Secrets Manager as an environment variable, so your code only reads os.environ. To read a secret from code instead:

import json
import boto3

def get_secret(secret_name: str) -> dict:
    client = boto3.client("secretsmanager")
    response = client.get_secret_value(SecretId=secret_name)
    return json.loads(response["SecretString"])

Logs

With the awslogs log driver in the container definition, everything the server prints goes to CloudWatch with no code changes:

"logConfiguration": {
  "logDriver": "awslogs",
  "options": {
    "awslogs-group": "/aws/ecs/mcp-server",
    "awslogs-region": "us-east-1",
    "awslogs-stream-prefix": "production"
  }
}

Deploy from GitHub Actions

This workflow runs your tests and nitrostack-py validate, pushes a new image to ECR, and restarts the ECS service on every push to main.

name: Deploy to AWS ECS

on:
  push:
    branches: [main]

jobs:
  deploy:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4

      - uses: actions/setup-python@v5
        with:
          python-version: "3.12"

      - name: Test and validate
        run: |
          pip install nitrostack -r requirements.txt pytest
          pytest
          nitrostack-py validate

      - name: Configure AWS credentials
        uses: aws-actions/configure-aws-credentials@v4
        with:
          aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }}
          aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
          aws-region: us-east-1

      - name: Login to ECR
        id: ecr
        uses: aws-actions/amazon-ecr-login@v2

      - name: Build and push
        run: |
          docker build -t mcp-server .
          docker tag mcp-server:latest ${{ steps.ecr.outputs.registry }}/mcp-server:latest
          docker push ${{ steps.ecr.outputs.registry }}/mcp-server:latest

      - name: Update ECS service
        run: |
          aws ecs update-service \
            --cluster mcp-cluster \
            --service mcp-server \
            --force-new-deployment

Next steps

On this page