Deploy Custom MCP Servers
Runlayer Deploy enables you to deploy custom MCP servers to managed infrastructure with automatic scaling, monitoring, and HTTPS endpoints. When to use Runlayer Deploy:- Deploy custom MCP servers not available in the catalog
- Build internal or proprietary integrations
- Run services requiring specific dependencies or configurations
Prerequisites
Before deploying, ensure you have:- Docker installed and running locally
- Runlayer CLI installed (
uvx runlayer) - Admin Permissions in the Runlayer platform
- A Dockerfile and application code ready to deploy
Quick Start
1
Initialize Deployment
Create a new deployment and generate configuration:This creates a deployment in the backend and generates a
runlayer.yaml template with your deployment ID.2
Configure Service
Edit the generated
runlayer.yaml file:- Set your service port
- Choose CPU and memory resources
- Add environment variables
- Configure build settings for your Dockerfile
3
Deploy
Build and deploy your service:
Runtimes
Docker
The Docker runtime builds your application locally using Docker and deploys it as a container. This gives you full control over dependencies and supports any language or framework. Build Configuration:dockerfile: Path to your Dockerfile (default:Dockerfile)context: Build context directory (default:.)platform: CPU architecture -x86orarm(default:arm, recommended for cost savings)args: Build arguments as key-value pairstarget: Target stage for multi-stage builds (optional)
Coming Soon
Managed Runtimes for Python and TypeScript—deploy with just your code and dependencies.
Configuration Reference
Completerunlayer.yaml structure:
Field Details
service.expose
Make specific paths publicly accessible without authentication. Supports wildcard patterns.
Examples:
['*']- Expose all paths['/public/*']- Expose all paths under/public['/api/v1/*', '/health']- Expose API v1 endpoints and health check
infrastructure
Valid CPU and memory combinations:
| CPU (vCPU) | Memory Options (MB) | Best For |
|---|---|---|
| 256 (.25 vCPU) | 512, 1024, 2048 | Minimal services |
| 512 (.5 vCPU) | 1024, 2048, 3072, 4096 | Light services |
| 1024 (1 vCPU) | 2048, 3072, 4096, 5120, 6144, 7168, 8192 | Standard services |
| 2048 (2 vCPU) | 4096-16384 (1024 MB increments) | Medium workloads |
| 4096 (4 vCPU) | 8192-30720 (1024 MB increments) | Heavy workloads |
See AWS Fargate pricing for cost estimates.
infrastructure.enable_db
Enable a managed database for persistent storage. When enabled, a DynamoDB table is provisioned with the following environment variables injected into your container:
DB_TABLE_NAME- The DynamoDB table nameDB_TABLE_REGION- The AWS region of the table
pk) and sort key (sk) schema. TTL is enabled via the ttl attribute (set to a Unix timestamp to auto-expire items).
Use any standard DynamoDB client to interact with the table—boto3 (Python), AWS SDK (Node.js), or any AWS SDK. All standard DynamoDB operations are supported: GetItem, PutItem, Query, Scan, BatchGetItem, BatchWriteItem, etc.
Example MCP tool with persistence (Python):
env
Environment variables are passed to your container at runtime. Values are automatically masked as *** in stored configurations and API responses for security.
CLI Variable Substitution:
The CLI supports shell-style environment variable substitution when loading runlayer.yaml, allowing you to reference local environment variables or .env files without hardcoding sensitive values.
Syntax:
${VAR}- Required variable (deployment fails if not set)${VAR:-default}- Use default value if variable is unset or empty${VAR-default}- Use default value only if variable is unset (not if empty)$$RUNLAYER_BASE_URL- Special backend variable - your deployment’s public URL (backend replaces at runtime)
$$RUNLAYER_BASE_URL is a special variable that resolves to your deployment’s public URL. Use it to construct URLs that reference your service.Remember: Add paths to service.expose to make them publicly accessible without authentication..env in your current working directory.
Automatic Environment Variables:
The backend automatically injects the following variable into your container:
RUNLAYER_BASE_URL- Your deployment’s public URL:https://your-platform.com/api/v1/proxy/hosted/{deployment_id}
$$RUNLAYER_BASE_URL (double dollar sign) as a placeholder in your YAML configuration. The backend replaces this at deployment time with your deployment’s actual public URL.
Example:
PUBLIC_ENDPOINT:https://your-platform.com/api/v1/proxy/hosted/{deployment_id}/public/api
CLI Commands
deploy init
Initialize a new deployment and generate configuration file.
- Creates a deployment record in your Runlayer instance
- Generates a
runlayer.yamltemplate with your deployment ID - Prompts for deployment name (must be lowercase and URL-friendly)
--config,-c: Path to create config file (default:runlayer.yaml)--secret,-s: Admin API key (required)--host,-H: Runlayer instance URL (required)
deploy
Build and deploy your service.
- Loads environment variables from
.env(auto-discovered) or--env-file - Substitutes variables in your YAML configuration
- Validates your YAML configuration
- Builds Docker image locally using your Dockerfile
- Pushes image to managed registry
- Deploys infrastructure
- Monitors deployment with live log streaming
--config,-c: Path to config file (default:runlayer.yaml)--secret,-s: Admin API key (required)--host,-H: Runlayer instance URL (required)--env-file,-e: Path to .env file for variable substitution (optional, auto-discovers.envby default)
deploy validate
Validate configuration without deploying.
--config,-c: Path to config file (default:runlayer.yaml)--secret,-s: Admin API key (required)--host,-H: Runlayer instance URL (required)--env-file,-e: Path to .env file for variable substitution (optional, auto-discovers.envby default)
deploy destroy
Teardown deployment and destroy infrastructure.
--config,-c: Path to config file containing deployment ID--deployment-id,-d: Deployment ID (overrides config file)--secret,-s: Admin API key (required)--host,-H: Runlayer instance URL (required)
Examples
Example: Multi-stage Node.js Build with legacy SSE transport
TypeScript MCP server with build arguments and optimized production build:Example: Environment Variable Substitution
Production service with secrets loaded from.env file:
runlayer.yaml:
Timing matters: CLI substitution (
${VAR}) happens before sending to backend. Backend substitution ($$RUNLAYER_BASE_URL) happens at deployment time. This means validation errors will show CLI-substituted values, but $$RUNLAYER_BASE_URL remains as-is until the backend deploys your service.Example: Service with Public Webhooks
Slack integration with publicly exposed webhook endpoints:Registering as an MCP Server
After your deployment succeeds, register it as an MCP server to make it available in your catalog:- Navigate to the deployment detail page in the Runlayer UI
- Click “Register as MCP Server” and update any information you need
- Click on “Create MCP”
- Your deployed service is now available as an MCP server in your catalog
Troubleshooting
Docker build fails
Docker build fails
Solutions:
- Test build locally first:
docker build -t test . - Check Dockerfile syntax and instructions
- Verify build context includes all required files
- Check build arguments are passed correctly
Invalid CPU/memory combination
Invalid CPU/memory combination
Solution:Refer to the configuration reference table for valid combinations. Not all CPU/memory pairs are supported. For example, 256 CPU only supports 512, 1024, or 2048 MB memory.
Deployment stuck or timeout
Deployment stuck or timeout
Solutions:Check deployment logs in the web UI. Common issues:
- Container failing health checks (check your app responds on the configured port)
- Wrong port configured in YAML
- Missing required environment variables
- Application crashes at startup (check application logs)
Can't destroy deployment
Can't destroy deployment
Solution:Disconnect all MCP servers using this deployment first. Go to the deployment detail page to view connected servers, then delete each server before destroying the deployment.
Environment variables not working
Environment variables not working
Explanation:Values are automatically masked as
*** in stored configurations and API responses for security. This is expected behavior. Your actual values are securely passed to the container at runtime and are not visible in the UI or API.Variable substitution errors
Variable substitution errors
Common issues:
- “Required environment variable ‘VAR’ is not set”: Variable isn’t in your environment or
.envfile. Check spelling and ensure it’s exported. - Wrong default syntax: Use
${VAR:-default}(with colon) to handle empty strings, or${VAR-default}(no colon) to only use default if unset. $$RUNLAYER_BASE_URLgetting substituted by CLI: Use double dollar sign ($$RUNLAYER_BASE_URL), not single (${RUNLAYER_BASE_URL}). Single dollar triggers CLI substitution, double dollar preserves it for backend replacement.
Build works locally but fails in deployment
Build works locally but fails in deployment
Solutions:
- Verify platform architecture matches (
x86vsarm) - Check all dependencies are in Dockerfile, not just on your local machine
- Ensure build arguments are specified in YAML if required
- Review build logs for missing packages or permission errors