Tools
Tools: Deploy Your MCP Server to Google Cloud Run (For Free)
2026-02-23
0 views
admin
Introduction ## Why Google Cloud Run? ## Scaffold Your MCP Server ## The Dockerfile ## Set Up the gcloud CLI ## Install gcloud CLI ## Enable Billing ## Deploy to Cloud Run ## Create a New Project ## Enable Required APIs ## Deploy from Source ## Get Your Service URL ## Troubleshooting: Invalid Host Error ## Test Your Deployed Server ## Cleanup ## What's Next? ## What About Stdio Servers? ## Conclusion ## Resources ## Related Articles You've built an MCP server. It works on localhost. Your AI assistant can call tools, fetch data, and do useful things, as long as everything runs on your machine. But what happens when you want to share it with your team? Or connect to it from a different device? Or just keep it running without your laptop open? You need to deploy it. This guide walks you through deploying a Streamable HTTP MCP server to Google Cloud Run, from scaffolding to a live URL in minutes. Streamable HTTP is the transport designed for remote deployments: it works over standard HTTPS, plays nicely with load balancers, and doesn't require the client to run your server as a subprocess. Note: If you're working with a stdio MCP server, the deployment path is different. We'll cover that briefly at the end of this article. If you've already built an MCP server using our first MCP server guide, you can deploy that project directly. Otherwise, we'll scaffold a fresh one below. Cloud Run can deploy from a container image, a Dockerfile, or even raw source code (using buildpacks). The create-mcp-server scaffold includes a Dockerfile, so that's the path we'll use. If you're bringing your own server, any of these options work. Here's why Cloud Run is a great fit for MCP servers: In short: you get a free, secure, production-ready deployment with almost zero configuration. If you already have an MCP server project with a Dockerfile, skip ahead to Set Up the gcloud CLI. Otherwise, let's scaffold a fresh one. Run: This creates a stateless MCP server using the Official TypeScript SDK. No interactive prompts, no choices needed. One command, one project. We're using a stateless server here for simplicity, but you can deploy a stateful server to Cloud Run the same way. The deployment steps are identical. The generated project structure: Install dependencies and verify it works locally: Your server is running at http://localhost:3000/mcp. Stop it with Ctrl+C. We're ready to deploy. Want to understand the scaffolded code in detail? See our Create Your First MCP Server guide. The scaffold includes a production-ready Dockerfile. Here's what it does: It's a multi-stage build: the first stage compiles TypeScript, the second stage copies only the compiled output and dependencies. This keeps the final image small. The important part: it exposes port 3000, which matches the --port 3000 flag we'll use when deploying. Cloud Build will use this Dockerfile automatically. You don't need Docker installed on your machine. Windows (via winget): Or download the installer. macOS (via Homebrew): After installing, restart your terminal and authenticate: Cloud Run requires a billing account, even for the free tier. You won't be charged if you stay within the free limits. Option 1: Via the console Option 2: Via CLI (if you already have a billing account): We recommend creating a dedicated project for this tutorial. This keeps your demo isolated from existing resources, so cleanup commands won't accidentally affect your other projects or container images. Project IDs must be globally unique. If my-mcp-project is taken, choose something else. This enables three services: Make sure you're in your project directory (my-mcp-server/), then run: Here's what each flag does: The first deploy takes a couple of minutes as Cloud Build pulls the base image and builds your container. Subsequent deploys are faster thanks to layer caching. This outputs something like: Your MCP endpoint is now live at https://<service-url>/mcp. If you get an error like "Invalid Host: my-mcp-server-abc123xyz.us-central1.run.app", it means the server is validating the Host header and rejecting the Cloud Run domain. Fix it by setting the ALLOWED_HOSTS environment variable: Replace the domain with your actual service URL (without https://). Let's verify everything works. Open a terminal and run: Once the inspector opens in your browser: You should see your server's tools listed. Click on any tool, fill in the parameters, and run it. That's it. Your MCP server is live on the internet, accessible from any MCP-compatible client, anywhere. If you want to remove the deployed resources: Note: Cloud Build stores your container image in Artifact Registry, which charges for storage after the first 500MB free. Deleting the project removes everything, but if you're keeping the project, clean up the images separately. If you're staying within the free tier, there's no urgency to clean up. But it's good practice to remove resources you no longer need. Now that your server is deployed, here are some ideas: Add authentication: secure your deployed server with OAuth using our OAuth for MCP Servers guide. Build real tools: replace the placeholder tools with your own by following our first MCP server guide. Try FastMCP: build with a different framework using our FastMCP guide. Connect your IDE: point your VS Code or Cursor MCP configuration at your Cloud Run URL instead of localhost. Set up a custom domain: configure a custom domain in Cloud Run for a cleaner URL. Everything in this guide applies to Streamable HTTP servers, the transport designed for cloud deployment. But not all MCP servers use HTTP. Stdio servers communicate via stdin/stdout and run as local subprocesses. They can't be deployed as web services. Instead, they're distributed so clients can run them locally: via npm (npx your-server), PyPI (uvx your-server), or Docker Hub (pull and run via Docker). We'll cover stdio distribution in detail in a future article. Stay tuned. You just went from a scaffolded project to a live, publicly accessible MCP server, in minutes, for free. No Docker installed locally, no infrastructure to manage, just a single gcloud run deploy --source . command. With Cloud Run, your MCP server is always available, scales automatically, and costs nothing while idle. That's a pretty good deal for getting your AI tools off localhost and into the real world. If you found create-mcp-server useful, consider giving it a star on GitHub and sharing it with others who are building MCP servers. It helps the project grow and helps more developers get started quickly. Enjoying content like this? Sign up for Agent Briefings, where I share insights and news on building and scaling MCP Servers and AI agents. Templates let you quickly answer FAQs or store snippets for re-use. Are you sure you want to hide this comment? It will become hidden in your post, but will still be visible via the comment's permalink. Hide child comments as well For further actions, you may consider blocking this person and/or reporting abuse COMMAND_BLOCK:
# For more options, see https://github.com/agentailor/create-mcp-server
npx @agentailor/create-mcp-server --name=my-mcp-server Enter fullscreen mode Exit fullscreen mode COMMAND_BLOCK:
# For more options, see https://github.com/agentailor/create-mcp-server
npx @agentailor/create-mcp-server --name=my-mcp-server COMMAND_BLOCK:
# For more options, see https://github.com/agentailor/create-mcp-server
npx @agentailor/create-mcp-server --name=my-mcp-server COMMAND_BLOCK:
my-mcp-server/
├── src/
│ ├── server.ts # MCP server (tools, prompts, resources)
│ └── index.ts # Express app and HTTP transport
├── Dockerfile # Production-ready Docker build
├── package.json
├── tsconfig.json
├── .env.example
├── .gitignore
└── README.md Enter fullscreen mode Exit fullscreen mode COMMAND_BLOCK:
my-mcp-server/
├── src/
│ ├── server.ts # MCP server (tools, prompts, resources)
│ └── index.ts # Express app and HTTP transport
├── Dockerfile # Production-ready Docker build
├── package.json
├── tsconfig.json
├── .env.example
├── .gitignore
└── README.md COMMAND_BLOCK:
my-mcp-server/
├── src/
│ ├── server.ts # MCP server (tools, prompts, resources)
│ └── index.ts # Express app and HTTP transport
├── Dockerfile # Production-ready Docker build
├── package.json
├── tsconfig.json
├── .env.example
├── .gitignore
└── README.md CODE_BLOCK:
cd my-mcp-server
npm install
npm run dev Enter fullscreen mode Exit fullscreen mode CODE_BLOCK:
cd my-mcp-server
npm install
npm run dev CODE_BLOCK:
cd my-mcp-server
npm install
npm run dev CODE_BLOCK:
MCP Stateless HTTP Server listening on port 3000 Enter fullscreen mode Exit fullscreen mode CODE_BLOCK:
MCP Stateless HTTP Server listening on port 3000 CODE_BLOCK:
MCP Stateless HTTP Server listening on port 3000 COMMAND_BLOCK:
# Multi-stage build for production
FROM node:20-alpine AS builder WORKDIR /app # Copy package files
COPY package.json package-lock.json ./ # Install all dependencies (including dev)
RUN npm ci # Copy source code
COPY . . # Build the application
RUN npm run build # Production stage
FROM node:20-alpine AS production WORKDIR /app # Copy package files
COPY package.json package-lock.json ./ # Install production dependencies only
RUN npm ci --omit=dev # Copy built application from builder stage
COPY --from=builder /app/dist ./dist # Expose the port the app runs on
EXPOSE 3000 # Start the application
CMD ["node", "dist/index.js"] Enter fullscreen mode Exit fullscreen mode COMMAND_BLOCK:
# Multi-stage build for production
FROM node:20-alpine AS builder WORKDIR /app # Copy package files
COPY package.json package-lock.json ./ # Install all dependencies (including dev)
RUN npm ci # Copy source code
COPY . . # Build the application
RUN npm run build # Production stage
FROM node:20-alpine AS production WORKDIR /app # Copy package files
COPY package.json package-lock.json ./ # Install production dependencies only
RUN npm ci --omit=dev # Copy built application from builder stage
COPY --from=builder /app/dist ./dist # Expose the port the app runs on
EXPOSE 3000 # Start the application
CMD ["node", "dist/index.js"] COMMAND_BLOCK:
# Multi-stage build for production
FROM node:20-alpine AS builder WORKDIR /app # Copy package files
COPY package.json package-lock.json ./ # Install all dependencies (including dev)
RUN npm ci # Copy source code
COPY . . # Build the application
RUN npm run build # Production stage
FROM node:20-alpine AS production WORKDIR /app # Copy package files
COPY package.json package-lock.json ./ # Install production dependencies only
RUN npm ci --omit=dev # Copy built application from builder stage
COPY --from=builder /app/dist ./dist # Expose the port the app runs on
EXPOSE 3000 # Start the application
CMD ["node", "dist/index.js"] CODE_BLOCK:
winget install Google.CloudSDK Enter fullscreen mode Exit fullscreen mode CODE_BLOCK:
winget install Google.CloudSDK CODE_BLOCK:
winget install Google.CloudSDK CODE_BLOCK:
brew install --cask google-cloud-sdk Enter fullscreen mode Exit fullscreen mode CODE_BLOCK:
brew install --cask google-cloud-sdk CODE_BLOCK:
brew install --cask google-cloud-sdk COMMAND_BLOCK:
curl -O https://dl.google.com/dl/cloudsdk/channels/rapid/downloads/google-cloud-cli-linux-x86_64.tar.gz
tar -xf google-cloud-cli-linux-x86_64.tar.gz
./google-cloud-sdk/install.sh Enter fullscreen mode Exit fullscreen mode COMMAND_BLOCK:
curl -O https://dl.google.com/dl/cloudsdk/channels/rapid/downloads/google-cloud-cli-linux-x86_64.tar.gz
tar -xf google-cloud-cli-linux-x86_64.tar.gz
./google-cloud-sdk/install.sh COMMAND_BLOCK:
curl -O https://dl.google.com/dl/cloudsdk/channels/rapid/downloads/google-cloud-cli-linux-x86_64.tar.gz
tar -xf google-cloud-cli-linux-x86_64.tar.gz
./google-cloud-sdk/install.sh CODE_BLOCK:
gcloud init
gcloud auth login Enter fullscreen mode Exit fullscreen mode CODE_BLOCK:
gcloud init
gcloud auth login CODE_BLOCK:
gcloud init
gcloud auth login CODE_BLOCK:
gcloud billing accounts list
gcloud billing projects link YOUR_PROJECT_ID --billing-account=YOUR_BILLING_ACCOUNT_ID Enter fullscreen mode Exit fullscreen mode CODE_BLOCK:
gcloud billing accounts list
gcloud billing projects link YOUR_PROJECT_ID --billing-account=YOUR_BILLING_ACCOUNT_ID CODE_BLOCK:
gcloud billing accounts list
gcloud billing projects link YOUR_PROJECT_ID --billing-account=YOUR_BILLING_ACCOUNT_ID CODE_BLOCK:
gcloud projects create my-mcp-project --name="My MCP Server"
gcloud config set project my-mcp-project Enter fullscreen mode Exit fullscreen mode CODE_BLOCK:
gcloud projects create my-mcp-project --name="My MCP Server"
gcloud config set project my-mcp-project CODE_BLOCK:
gcloud projects create my-mcp-project --name="My MCP Server"
gcloud config set project my-mcp-project CODE_BLOCK:
gcloud services enable run.googleapis.com artifactregistry.googleapis.com cloudbuild.googleapis.com Enter fullscreen mode Exit fullscreen mode CODE_BLOCK:
gcloud services enable run.googleapis.com artifactregistry.googleapis.com cloudbuild.googleapis.com CODE_BLOCK:
gcloud services enable run.googleapis.com artifactregistry.googleapis.com cloudbuild.googleapis.com CODE_BLOCK:
gcloud run deploy my-mcp-server \ --source . \ --region us-central1 \ --allow-unauthenticated \ --port 3000 Enter fullscreen mode Exit fullscreen mode CODE_BLOCK:
gcloud run deploy my-mcp-server \ --source . \ --region us-central1 \ --allow-unauthenticated \ --port 3000 CODE_BLOCK:
gcloud run deploy my-mcp-server \ --source . \ --region us-central1 \ --allow-unauthenticated \ --port 3000 CODE_BLOCK:
gcloud run services describe my-mcp-server --region us-central1 --format='value(status.url)' Enter fullscreen mode Exit fullscreen mode CODE_BLOCK:
gcloud run services describe my-mcp-server --region us-central1 --format='value(status.url)' CODE_BLOCK:
gcloud run services describe my-mcp-server --region us-central1 --format='value(status.url)' CODE_BLOCK:
https://my-mcp-server-abc123xyz.us-central1.run.app Enter fullscreen mode Exit fullscreen mode CODE_BLOCK:
https://my-mcp-server-abc123xyz.us-central1.run.app CODE_BLOCK:
https://my-mcp-server-abc123xyz.us-central1.run.app CODE_BLOCK:
gcloud run services update my-mcp-server \ --region us-central1 \ --set-env-vars ALLOWED_HOSTS=my-mcp-server-abc123xyz.us-central1.run.app Enter fullscreen mode Exit fullscreen mode CODE_BLOCK:
gcloud run services update my-mcp-server \ --region us-central1 \ --set-env-vars ALLOWED_HOSTS=my-mcp-server-abc123xyz.us-central1.run.app CODE_BLOCK:
gcloud run services update my-mcp-server \ --region us-central1 \ --set-env-vars ALLOWED_HOSTS=my-mcp-server-abc123xyz.us-central1.run.app CODE_BLOCK:
npx @modelcontextprotocol/inspector Enter fullscreen mode Exit fullscreen mode CODE_BLOCK:
npx @modelcontextprotocol/inspector CODE_BLOCK:
npx @modelcontextprotocol/inspector COMMAND_BLOCK:
# Delete the Cloud Run service
gcloud run services delete my-mcp-server --region us-central1 # Delete the container image from Artifact Registry
gcloud artifacts docker images list us-central1-docker.pkg.dev/my-mcp-project --format='value(IMAGE)' | xargs -I {} gcloud artifacts docker images delete {} --quiet # Optionally delete the entire project (removes everything)
gcloud projects delete my-mcp-project Enter fullscreen mode Exit fullscreen mode COMMAND_BLOCK:
# Delete the Cloud Run service
gcloud run services delete my-mcp-server --region us-central1 # Delete the container image from Artifact Registry
gcloud artifacts docker images list us-central1-docker.pkg.dev/my-mcp-project --format='value(IMAGE)' | xargs -I {} gcloud artifacts docker images delete {} --quiet # Optionally delete the entire project (removes everything)
gcloud projects delete my-mcp-project COMMAND_BLOCK:
# Delete the Cloud Run service
gcloud run services delete my-mcp-server --region us-central1 # Delete the container image from Artifact Registry
gcloud artifacts docker images list us-central1-docker.pkg.dev/my-mcp-project --format='value(IMAGE)' | xargs -I {} gcloud artifacts docker images delete {} --quiet # Optionally delete the entire project (removes everything)
gcloud projects delete my-mcp-project - How to scaffold a deployment-ready MCP server
- How to set up the Google Cloud CLI
- How to deploy to Cloud Run with a single command
- How to test your live server with MCP Inspector - Node.js 20 or later
- A Google Cloud account (free to create)
- Basic terminal familiarity - Generous free tier — 2 million requests/month, 180,000 vCPU-seconds, and 360,000 GiB-seconds of memory. More than enough for development, testing, and light production use.
- Build from source — No need to install Docker locally. Cloud Run uses Cloud Build to build your Dockerfile remotely.
- HTTPS by default — Every deployed service gets an HTTPS URL automatically. MCP clients expect HTTPS for remote servers.
- Scale to zero — When no one is calling your server, it scales down to zero instances. You pay nothing when idle.
- No code changes — Streamable HTTP servers work as-is on Cloud Run. The POST /mcp endpoint maps directly to Cloud Run's request-based model. - Go to Google Cloud Billing
- Click Link a billing account (or Create account if you don't have one)
- Add a payment method
- Select your project and link it to the billing account - Cloud Run: hosts your container
- Artifact Registry: stores your container image
- Cloud Build: builds your Dockerfile remotely - --source .: sends your source code to Cloud Build, which builds the container using your Dockerfile
- --region us-central1: deploys to a US region (or choose another region closer to you)
- --allow-unauthenticated: makes your /mcp endpoint publicly accessible
- --port 3000: tells Cloud Run which port your container listens on - Change the transport type to Streamable HTTP
- Enter your Cloud Run URL: https://<service-url>/mcp
- Click Connect - create-mcp-server (GitHub): the CLI tool used in this guide
- Google Cloud Run Documentation: official Cloud Run docs
- Google Cloud Free Tier: free tier details and limits
- MCP Inspector (GitHub): testing and debugging tool for MCP servers
- MCP Documentation: official protocol specification
- gcloud CLI Reference: CLI documentation - Create Your First MCP Server in 5 Minutes
- Securing MCP Servers with OAuth and Keycloak
- Getting Started with FastMCP
how-totutorialguidedev.toailinuxserverdockernodegitgithub