Remote Deployment
This guide provides comprehensive guidance for securely deploying and operating Model Context Protocol (MCP) servers on remote infrastructure. Remote deployments require specific security considerations to ensure safe operation while maintaining performance and reliability.
Community Discussion
💬 Remote Deployment Discussions - Share your remote deployment strategies, infrastructure configurations, and operational experiences with the community.
Why Remote Deployment?
Benefits of Remote MCP Deployment
- Resource Isolation - Separate MCP servers from development and production systems
- Scalability - Easy horizontal scaling of MCP server instances
- Cost Efficiency - Use cloud resources only when needed
- Security Boundaries - Isolate AI agent operations from corporate networks
- Compliance - Meet regulatory requirements for data processing locations
Remote Deployment Challenges
- Network Security - Secure communication over public networks
- Access Control - Manage remote access and authentication
- Monitoring - Comprehensive monitoring of remote systems
- Incident Response - Responding to security incidents on remote infrastructure
VPS Security Hardening
Initial VPS Setup
#!/bin/bash
# Secure VPS setup for MCP deployment
# Update system
apt update && apt upgrade -y
# Create non-root user for MCP
useradd -m -s /bin/bash mcp-user
usermod -aG sudo mcp-user
# Configure SSH security
sed -i 's/PermitRootLogin yes/PermitRootLogin no/' /etc/ssh/sshd_config
sed -i 's/#PasswordAuthentication yes/PasswordAuthentication no/' /etc/ssh/sshd_config
echo "AllowUsers mcp-user" >> /etc/ssh/sshd_config
systemctl reload ssh
# Install security tools
apt install -y fail2ban ufw unattended-upgrades
# Configure firewall
ufw default deny incoming
ufw default allow outgoing
ufw allow ssh
ufw allow 8080/tcp # MCP server port
ufw --force enable
# Configure fail2ban
cp /etc/fail2ban/jail.conf /etc/fail2ban/jail.local
sed -i 's/bantime = 10m/bantime = 1h/' /etc/fail2ban/jail.local
sed -i 's/maxretry = 5/maxretry = 3/' /etc/fail2ban/jail.local
systemctl enable fail2ban
systemctl start fail2ban
# Enable automatic security updates
dpkg-reconfigure -plow unattended-upgrades
File System Security
#!/bin/bash
# Secure file system configuration
# Create secure directory structure
mkdir -p /opt/mcp/{config,logs,data}
chown -R mcp-user:mcp-user /opt/mcp
chmod 750 /opt/mcp/config
chmod 755 /opt/mcp/logs
chmod 700 /opt/mcp/data
# Set up secure temporary directory
mkdir -p /opt/mcp/tmp
chmod 700 /opt/mcp/tmp
echo "export TMPDIR=/opt/mcp/tmp" >> /home/mcp-user/.bashrc
# Configure log rotation
cat > /etc/logrotate.d/mcp << EOF
/opt/mcp/logs/*.log {
daily
rotate 30
compress
delaycompress
missingok
notifempty
create 644 mcp-user mcp-user
}
EOF
Container Deployment on Remote Systems
Docker Compose for Remote Deployment
# docker-compose.yml - Remote MCP deployment
version: '3.8'
services:
mcp-server:
image: mcp-server:latest
container_name: mcp-server
restart: unless-stopped
user: "1000:1000" # Non-root user
networks:
- mcp-network
ports:
- "127.0.0.1:8080:8080" # Bind to localhost only
environment:
- MCP_ENV=production
- MCP_LOG_LEVEL=info
- HTTPS_PROXY=http://api-gateway:8080
volumes:
- ./config:/app/config:ro
- ./logs:/app/logs:rw
- ./data:/app/data:rw
healthcheck:
test: ["CMD", "curl", "-f", "http://localhost:8080/health"]
interval: 30s
timeout: 10s
retries: 3
deploy:
resources:
limits:
cpus: '1.0'
memory: 1G
reservations:
cpus: '0.5'
memory: 512M
security_opt:
- no-new-privileges:true
cap_drop:
- ALL
cap_add:
- NET_BIND_SERVICE
read_only: true
tmpfs:
- /tmp
- /var/tmp
api-gateway:
image: kong:latest
container_name: api-gateway
restart: unless-stopped
networks:
- mcp-network
ports:
- "8080:8000"
environment:
- KONG_DATABASE=off
- KONG_DECLARATIVE_CONFIG=/kong/declarative/kong.yml
- KONG_PROXY_ACCESS_LOG=/dev/stdout
- KONG_ADMIN_ACCESS_LOG=/dev/stdout
volumes:
- ./kong.yml:/kong/declarative/kong.yml:ro
monitoring:
image: prom/prometheus:latest
container_name: monitoring
restart: unless-stopped
networks:
- mcp-network
ports:
- "127.0.0.1:9090:9090"
volumes:
- ./prometheus.yml:/etc/prometheus/prometheus.yml:ro
- prometheus_data:/prometheus
command:
- '--config.file=/etc/prometheus/prometheus.yml'
- '--storage.tsdb.path=/prometheus'
- '--web.console.libraries=/etc/prometheus/console_libraries'
- '--web.console.templates=/etc/prometheus/consoles'
networks:
mcp-network:
driver: bridge
internal: false
volumes:
prometheus_data:
Kubernetes Deployment
# k8s-deployment.yaml - Kubernetes deployment for MCP
apiVersion: apps/v1
kind: Deployment
metadata:
name: mcp-server
namespace: mcp-system
spec:
replicas: 3
selector:
matchLabels:
app: mcp-server
template:
metadata:
labels:
app: mcp-server
spec:
securityContext:
runAsNonRoot: true
runAsUser: 1000
fsGroup: 1000
containers:
- name: mcp-server
image: mcp-server:latest
ports:
- containerPort: 8080
env:
- name: MCP_ENV
value: "production"
- name: HTTPS_PROXY
value: "http://api-gateway:8080"
resources:
requests:
memory: "256Mi"
cpu: "250m"
limits:
memory: "512Mi"
cpu: "500m"
securityContext:
allowPrivilegeEscalation: false
readOnlyRootFilesystem: true
capabilities:
drop:
- ALL
livenessProbe:
httpGet:
path: /health
port: 8080
initialDelaySeconds: 30
periodSeconds: 10
readinessProbe:
httpGet:
path: /ready
port: 8080
initialDelaySeconds: 5
periodSeconds: 5
volumeMounts:
- name: config
mountPath: /app/config
readOnly: true
- name: logs
mountPath: /app/logs
- name: tmp
mountPath: /tmp
volumes:
- name: config
configMap:
name: mcp-config
- name: logs
emptyDir: {}
- name: tmp
emptyDir: {}
---
apiVersion: v1
kind: Service
metadata:
name: mcp-server
namespace: mcp-system
spec:
selector:
app: mcp-server
ports:
- port: 8080
targetPort: 8080
type: ClusterIP
Cloud Provider Security
AWS Security Configuration
#!/bin/bash
# AWS security configuration for MCP deployment
# Create security group
aws ec2 create-security-group \
--group-name mcp-security-group \
--description "Security group for MCP server" \
--vpc-id vpc-12345678
# Allow SSH from specific IP
aws ec2 authorize-security-group-ingress \
--group-id sg-12345678 \
--protocol tcp \
--port 22 \
--cidr YOUR_IP/32
# Allow MCP server port from load balancer
aws ec2 authorize-security-group-ingress \
--group-id sg-12345678 \
--protocol tcp \
--port 8080 \
--source-group sg-87654321
# Create IAM role for MCP server
aws iam create-role \
--role-name mcp-server-role \
--assume-role-policy-document file://trust-policy.json
# Attach minimal permissions policy
aws iam attach-role-policy \
--role-name mcp-server-role \
--policy-arn arn:aws:iam::aws:policy/CloudWatchAgentServerPolicy
# Create instance profile
aws iam create-instance-profile \
--instance-profile-name mcp-server-profile
aws iam add-role-to-instance-profile \
--instance-profile-name mcp-server-profile \
--role-name mcp-server-role
GCP Security Configuration
#!/bin/bash
# GCP security configuration for MCP deployment
# Create firewall rules
gcloud compute firewall-rules create mcp-allow-ssh \
--allow tcp:22 \
--source-ranges YOUR_IP/32 \
--target-tags mcp-server
gcloud compute firewall-rules create mcp-allow-http \
--allow tcp:8080 \
--source-tags load-balancer \
--target-tags mcp-server
# Create service account
gcloud iam service-accounts create mcp-server-sa \
--display-name "MCP Server Service Account"
# Grant minimal permissions
gcloud projects add-iam-policy-binding PROJECT_ID \
--member "serviceAccount:mcp-server-sa@PROJECT_ID.iam.gserviceaccount.com" \
--role "roles/monitoring.metricWriter"
# Create instance template
gcloud compute instance-templates create mcp-server-template \
--image-family ubuntu-2004-lts \
--image-project ubuntu-os-cloud \
--machine-type n1-standard-1 \
--service-account mcp-server-sa@PROJECT_ID.iam.gserviceaccount.com \
--scopes https://www.googleapis.com/auth/monitoring.write \
--tags mcp-server
Network Security for Remote Deployments
VPN Configuration
#!/bin/bash
# WireGuard VPN setup for secure remote access
# Install WireGuard
apt install -y wireguard
# Generate keys
wg genkey | tee /etc/wireguard/privatekey | wg pubkey | tee /etc/wireguard/publickey
# Create server configuration
cat > /etc/wireguard/wg0.conf << EOF
[Interface]
PrivateKey = $(cat /etc/wireguard/privatekey)
Address = 10.0.0.1/24
ListenPort = 51820
PostUp = iptables -A FORWARD -i wg0 -j ACCEPT; iptables -t nat -A POSTROUTING -o eth0 -j MASQUERADE
PostDown = iptables -D FORWARD -i wg0 -j ACCEPT; iptables -t nat -D POSTROUTING -o eth0 -j MASQUERADE
[Peer]
PublicKey = CLIENT_PUBLIC_KEY
AllowedIPs = 10.0.0.2/32
EOF
# Enable and start WireGuard
systemctl enable wg-quick@wg0
systemctl start wg-quick@wg0
# Configure firewall
ufw allow 51820/udp
SSH Hardening
#!/bin/bash
# SSH hardening for remote access
# Generate SSH keys
ssh-keygen -t ed25519 -f /etc/ssh/ssh_host_ed25519_key -N ""
ssh-keygen -t rsa -b 4096 -f /etc/ssh/ssh_host_rsa_key -N ""
# Configure SSH
cat > /etc/ssh/sshd_config << EOF
Port 2222
Protocol 2
HostKey /etc/ssh/ssh_host_ed25519_key
HostKey /etc/ssh/ssh_host_rsa_key
# Authentication
PubkeyAuthentication yes
AuthorizedKeysFile .ssh/authorized_keys
PasswordAuthentication no
PermitEmptyPasswords no
ChallengeResponseAuthentication no
UsePAM no
# Security settings
PermitRootLogin no
MaxAuthTries 3
MaxSessions 2
LoginGraceTime 30
ClientAliveInterval 300
ClientAliveCountMax 2
# Restrict users
AllowUsers mcp-user
# Disable dangerous features
X11Forwarding no
AllowTcpForwarding no
PermitTunnel no
EOF
systemctl reload ssh
Monitoring and Alerting
Prometheus Configuration
# prometheus.yml - Monitoring configuration
global:
scrape_interval: 15s
evaluation_interval: 15s
rule_files:
- "alert_rules.yml"
scrape_configs:
- job_name: 'mcp-server'
static_configs:
- targets: ['mcp-server:8080']
metrics_path: '/metrics'
scrape_interval: 5s
- job_name: 'node-exporter'
static_configs:
- targets: ['localhost:9100']
- job_name: 'api-gateway'
static_configs:
- targets: ['api-gateway:8001']
metrics_path: '/metrics'
alerting:
alertmanagers:
- static_configs:
- targets:
- alertmanager:9093
Alert Rules
# alert_rules.yml - Alert rules for MCP deployment
groups:
- name: mcp-server
rules:
- alert: MCPServerDown
expr: up{job="mcp-server"} == 0
for: 1m
labels:
severity: critical
annotations:
summary: "MCP Server is down"
description: "MCP Server has been down for more than 1 minute"
- alert: HighCPUUsage
expr: rate(process_cpu_seconds_total{job="mcp-server"}[5m]) > 0.8
for: 5m
labels:
severity: warning
annotations:
summary: "High CPU usage on MCP Server"
description: "CPU usage is above 80% for more than 5 minutes"
- alert: HighMemoryUsage
expr: process_resident_memory_bytes{job="mcp-server"} > 1073741824
for: 5m
labels:
severity: warning
annotations:
summary: "High memory usage on MCP Server"
description: "Memory usage is above 1GB for more than 5 minutes"
Security Monitoring
Log Aggregation
#!/bin/bash
# Set up centralized logging
# Install rsyslog
apt install -y rsyslog
# Configure remote logging
cat >> /etc/rsyslog.conf << EOF
# Remote logging configuration
*.* @@log-server:514
EOF
# Configure log rotation
cat > /etc/logrotate.d/mcp-remote << EOF
/var/log/mcp-remote/*.log {
daily
rotate 30
compress
delaycompress
missingok
notifempty
create 644 syslog adm
}
EOF
systemctl restart rsyslog
Intrusion Detection
#!/bin/bash
# Install and configure AIDE for file integrity monitoring
# Install AIDE
apt install -y aide
# Initialize AIDE database
aideinit
# Configure AIDE rules
cat > /etc/aide/aide.conf << EOF
# AIDE configuration for MCP server
@@define DBDIR /var/lib/aide
@@define LOGDIR /var/log/aide
database=file:@@{DBDIR}/aide.db
database_out=file:@@{DBDIR}/aide.db.new
gzip_dbout=yes
# Rules
/opt/mcp/config R
/opt/mcp/data R
/etc R
/usr/bin R
/usr/sbin R
EOF
# Set up daily checks
cat > /etc/cron.daily/aide << EOF
#!/bin/bash
/usr/bin/aide --check
EOF
chmod +x /etc/cron.daily/aide
Backup and Recovery
Automated Backup Script
#!/bin/bash
# Automated backup script for MCP deployment
BACKUP_DIR="/opt/backups"
DATE=$(date +%Y%m%d_%H%M%S)
BACKUP_FILE="mcp_backup_${DATE}.tar.gz"
# Create backup directory
mkdir -p $BACKUP_DIR
# Backup MCP configuration and data
tar -czf $BACKUP_DIR/$BACKUP_FILE \
/opt/mcp/config \
/opt/mcp/data \
/etc/nginx \
/etc/docker \
/home/mcp-user/.ssh
# Upload to cloud storage (AWS S3 example)
aws s3 cp $BACKUP_DIR/$BACKUP_FILE s3://mcp-backups/
# Keep only last 7 days of local backups
find $BACKUP_DIR -name "mcp_backup_*.tar.gz" -mtime +7 -delete
# Log backup completion
echo "$(date): Backup completed - $BACKUP_FILE" >> /var/log/mcp-backup.log
Disaster Recovery Plan
#!/bin/bash
# Disaster recovery script for MCP deployment
# Stop services
docker-compose down
# Restore from backup
BACKUP_FILE=$1
if [ -z "$BACKUP_FILE" ]; then
echo "Usage: $0 <backup_file>"
exit 1
fi
# Extract backup
tar -xzf $BACKUP_FILE -C /
# Restore file permissions
chown -R mcp-user:mcp-user /opt/mcp
chmod 750 /opt/mcp/config
chmod 700 /opt/mcp/data
# Restart services
docker-compose up -d
# Verify services
sleep 30
curl -f http://localhost:8080/health || echo "Health check failed"
Contributing
Help improve our remote deployment guidance by sharing:
- Infrastructure Templates - Terraform, CloudFormation, and other IaC templates
- Security Configurations - Hardening scripts and security configurations
- Monitoring Setups - Comprehensive monitoring and alerting configurations
- Operational Procedures - Deployment, backup, and recovery procedures
This page is being developed with community input. Share your remote deployment experience in our discussions.