# Production Deployment Guide
## Overview
This guide covers deploying the MCP Calendar Server to production environments including Docker, Kubernetes, and cloud platforms.
## Prerequisites
- Docker and Docker Compose
- PostgreSQL 12+ database
- Redis 6+ (optional but recommended)
- Google Cloud Project with Calendar API enabled
- SSL certificate for HTTPS
## Environment Configurations
### Production Environment Variables
```bash
# Server Configuration
NODE_ENV=production
PORT=3000
HOST=0.0.0.0
# Database (Use connection pooling service like PgBouncer)
DATABASE_URL=postgresql://username:password@db.example.com:5432/calendar_assistant
DATABASE_SSL=true
# Redis Cluster
REDIS_URL=redis://redis.example.com:6379
# Google Calendar API
GOOGLE_CLIENT_ID=your_production_client_id
GOOGLE_CLIENT_SECRET=your_production_client_secret
GOOGLE_REDIRECT_URI=https://your-domain.com/auth/google/callback
# Security
JWT_SECRET=your_super_secure_jwt_secret_32_chars_plus
WEBHOOK_SECRET=your_webhook_verification_secret
# Rate Limiting (More restrictive in production)
RATE_LIMIT_WINDOW_MS=900000
RATE_LIMIT_MAX_REQUESTS=100
# Logging
LOG_LEVEL=info
LOG_FORMAT=json
LOG_FILE_ENABLED=true
LOG_FILE_NAME=/app/logs/calendar-server.log
# Features (Enable all in production)
FEATURE_CACHING=true
FEATURE_BATCHING=true
FEATURE_PREFETCHING=true
FEATURE_ANALYTICS=true
# Monitoring
ALERT_WEBHOOK_URL=https://your-monitoring.com/webhooks/alerts
# CORS (Restrict to your domains)
CORS_ORIGIN=https://your-app.com,https://admin.your-app.com
```
## Docker Production Deployment
### 1. Multi-stage Production Dockerfile
```dockerfile
# Build stage
FROM node:18-alpine AS builder
WORKDIR /app
COPY package*.json ./
RUN npm ci --only=production && npm cache clean --force
COPY . .
RUN npm run build
# Production stage
FROM node:18-alpine AS production
RUN addgroup -g 1001 -S nodejs && adduser -S nodejs -u 1001
WORKDIR /app
# Copy built application
COPY --from=builder --chown=nodejs:nodejs /app/dist ./dist
COPY --from=builder --chown=nodejs:nodejs /app/node_modules ./node_modules
COPY --from=builder --chown=nodejs:nodejs /app/package.json ./
# Create logs directory
RUN mkdir -p logs && chown -R nodejs:nodejs logs
USER nodejs
EXPOSE 3000
HEALTHCHECK --interval=30s --timeout=10s --start-period=5s --retries=3 \
CMD node -e "require('http').get('http://localhost:3000/health', (res) => { process.exit(res.statusCode === 200 ? 0 : 1) })"
CMD ["node", "dist/index.js"]
```
### 2. Production Docker Compose
```yaml
version: '3.8'
services:
app:
build:
context: .
dockerfile: deployment/Dockerfile.prod
target: production
restart: unless-stopped
environment:
- NODE_ENV=production
- DATABASE_URL=postgresql://calendar_user:${DB_PASSWORD}@postgres:5432/calendar_assistant
- REDIS_URL=redis://redis:6379
- GOOGLE_CLIENT_ID=${GOOGLE_CLIENT_ID}
- GOOGLE_CLIENT_SECRET=${GOOGLE_CLIENT_SECRET}
- JWT_SECRET=${JWT_SECRET}
depends_on:
postgres:
condition: service_healthy
redis:
condition: service_healthy
networks:
- app-network
volumes:
- app-logs:/app/logs
deploy:
replicas: 3
resources:
limits:
memory: 512M
cpus: '0.5'
reservations:
memory: 256M
cpus: '0.25'
postgres:
image: postgres:15-alpine
restart: unless-stopped
environment:
- POSTGRES_DB=calendar_assistant
- POSTGRES_USER=calendar_user
- POSTGRES_PASSWORD=${DB_PASSWORD}
- POSTGRES_INITDB_ARGS=--auth-host=md5
volumes:
- postgres-data:/var/lib/postgresql/data
- ./deployment/postgres-init.sql:/docker-entrypoint-initdb.d/init.sql
networks:
- app-network
healthcheck:
test: ["CMD-SHELL", "pg_isready -U calendar_user -d calendar_assistant"]
interval: 10s
timeout: 5s
retries: 5
redis:
image: redis:7-alpine
restart: unless-stopped
command: redis-server --appendonly yes --maxmemory 256mb --maxmemory-policy allkeys-lru
volumes:
- redis-data:/data
networks:
- app-network
healthcheck:
test: ["CMD", "redis-cli", "ping"]
interval: 10s
timeout: 5s
retries: 5
nginx:
image: nginx:alpine
restart: unless-stopped
ports:
- "80:80"
- "443:443"
volumes:
- ./deployment/nginx.conf:/etc/nginx/nginx.conf:ro
- ./deployment/ssl:/etc/ssl/certs:ro
- app-logs:/var/log/app:ro
depends_on:
- app
networks:
- app-network
prometheus:
image: prom/prometheus:latest
restart: unless-stopped
volumes:
- ./deployment/prometheus.yml:/etc/prometheus/prometheus.yml:ro
- prometheus-data:/prometheus
ports:
- "9090:9090"
networks:
- monitoring-network
grafana:
image: grafana/grafana:latest
restart: unless-stopped
environment:
- GF_SECURITY_ADMIN_PASSWORD=${GRAFANA_PASSWORD}
volumes:
- grafana-data:/var/lib/grafana
- ./deployment/grafana/dashboards:/etc/grafana/provisioning/dashboards:ro
ports:
- "3001:3000"
networks:
- monitoring-network
volumes:
postgres-data:
redis-data:
app-logs:
prometheus-data:
grafana-data:
networks:
app-network:
driver: bridge
monitoring-network:
driver: bridge
```
## Kubernetes Deployment
### 1. Namespace and ConfigMap
```yaml
# namespace.yaml
apiVersion: v1
kind: Namespace
metadata:
name: mcp-calendar
---
# configmap.yaml
apiVersion: v1
kind: ConfigMap
metadata:
name: calendar-config
namespace: mcp-calendar
data:
NODE_ENV: "production"
LOG_LEVEL: "info"
LOG_FORMAT: "json"
FEATURE_CACHING: "true"
FEATURE_BATCHING: "true"
RATE_LIMIT_WINDOW_MS: "900000"
RATE_LIMIT_MAX_REQUESTS: "100"
```
### 2. Secrets
```yaml
# secrets.yaml
apiVersion: v1
kind: Secret
metadata:
name: calendar-secrets
namespace: mcp-calendar
type: Opaque
data:
DATABASE_URL: <base64-encoded-database-url>
REDIS_URL: <base64-encoded-redis-url>
GOOGLE_CLIENT_ID: <base64-encoded-client-id>
GOOGLE_CLIENT_SECRET: <base64-encoded-client-secret>
JWT_SECRET: <base64-encoded-jwt-secret>
```
### 3. Deployment
```yaml
# deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: calendar-server
namespace: mcp-calendar
labels:
app: calendar-server
spec:
replicas: 3
selector:
matchLabels:
app: calendar-server
template:
metadata:
labels:
app: calendar-server
spec:
containers:
- name: calendar-server
image: your-registry/mcp-calendar-server:latest
ports:
- containerPort: 3000
envFrom:
- configMapRef:
name: calendar-config
- secretRef:
name: calendar-secrets
resources:
requests:
memory: "256Mi"
cpu: "250m"
limits:
memory: "512Mi"
cpu: "500m"
livenessProbe:
httpGet:
path: /health/live
port: 3000
initialDelaySeconds: 30
periodSeconds: 30
readinessProbe:
httpGet:
path: /health/ready
port: 3000
initialDelaySeconds: 5
periodSeconds: 10
volumeMounts:
- name: logs
mountPath: /app/logs
volumes:
- name: logs
emptyDir: {}
```
### 4. Service and Ingress
```yaml
# service.yaml
apiVersion: v1
kind: Service
metadata:
name: calendar-service
namespace: mcp-calendar
spec:
selector:
app: calendar-server
ports:
- port: 80
targetPort: 3000
type: ClusterIP
---
# ingress.yaml
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: calendar-ingress
namespace: mcp-calendar
annotations:
nginx.ingress.kubernetes.io/rewrite-target: /
cert-manager.io/cluster-issuer: letsencrypt-prod
spec:
tls:
- hosts:
- calendar.your-domain.com
secretName: calendar-tls
rules:
- host: calendar.your-domain.com
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: calendar-service
port:
number: 80
```
## Cloud Platform Deployments
### AWS ECS with Fargate
```json
{
"family": "mcp-calendar-server",
"networkMode": "awsvpc",
"requiresCompatibilities": ["FARGATE"],
"cpu": "512",
"memory": "1024",
"executionRoleArn": "arn:aws:iam::account:role/ecsTaskExecutionRole",
"taskRoleArn": "arn:aws:iam::account:role/ecsTaskRole",
"containerDefinitions": [
{
"name": "calendar-server",
"image": "your-account.dkr.ecr.region.amazonaws.com/mcp-calendar-server:latest",
"portMappings": [
{
"containerPort": 3000,
"protocol": "tcp"
}
],
"essential": true,
"environment": [
{
"name": "NODE_ENV",
"value": "production"
}
],
"secrets": [
{
"name": "DATABASE_URL",
"valueFrom": "arn:aws:secretsmanager:region:account:secret:calendar/database-url"
},
{
"name": "JWT_SECRET",
"valueFrom": "arn:aws:secretsmanager:region:account:secret:calendar/jwt-secret"
}
],
"logConfiguration": {
"logDriver": "awslogs",
"options": {
"awslogs-group": "/ecs/mcp-calendar-server",
"awslogs-region": "us-east-1",
"awslogs-stream-prefix": "ecs"
}
},
"healthCheck": {
"command": [
"CMD-SHELL",
"curl -f http://localhost:3000/health || exit 1"
],
"interval": 30,
"timeout": 5,
"retries": 3
}
}
]
}
```
### Google Cloud Run
```yaml
# cloudrun.yaml
apiVersion: serving.knative.dev/v1
kind: Service
metadata:
name: mcp-calendar-server
annotations:
run.googleapis.com/ingress: all
spec:
template:
metadata:
annotations:
autoscaling.knative.dev/maxScale: "10"
run.googleapis.com/cpu-throttling: "false"
spec:
containerConcurrency: 100
containers:
- image: gcr.io/your-project/mcp-calendar-server:latest
ports:
- containerPort: 3000
env:
- name: NODE_ENV
value: "production"
- name: DATABASE_URL
valueFrom:
secretKeyRef:
name: calendar-secrets
key: database-url
resources:
limits:
cpu: "1"
memory: "512Mi"
livenessProbe:
httpGet:
path: /health/live
port: 3000
startupProbe:
httpGet:
path: /health/ready
port: 3000
failureThreshold: 10
periodSeconds: 5
```
## Monitoring and Observability
### Prometheus Configuration
```yaml
# prometheus.yml
global:
scrape_interval: 15s
scrape_configs:
- job_name: 'mcp-calendar-server'
static_configs:
- targets: ['app:3000']
metrics_path: '/metrics'
scrape_interval: 30s
- job_name: 'postgres'
static_configs:
- targets: ['postgres:5432']
- job_name: 'redis'
static_configs:
- targets: ['redis:6379']
```
### Grafana Dashboard
```json
{
"dashboard": {
"title": "MCP Calendar Server",
"panels": [
{
"title": "Request Rate",
"type": "graph",
"targets": [
{
"expr": "rate(http_requests_total[5m])"
}
]
},
{
"title": "Response Time",
"type": "graph",
"targets": [
{
"expr": "histogram_quantile(0.95, rate(http_request_duration[5m]))"
}
]
},
{
"title": "Error Rate",
"type": "graph",
"targets": [
{
"expr": "rate(http_errors_total[5m]) / rate(http_requests_total[5m])"
}
]
}
]
}
}
```
## Security Checklist
- [ ] Use HTTPS in production
- [ ] Set secure JWT secrets (32+ characters)
- [ ] Enable database SSL connections
- [ ] Configure proper CORS origins
- [ ] Set up rate limiting
- [ ] Use secrets management for sensitive data
- [ ] Enable container security scanning
- [ ] Set up network policies in Kubernetes
- [ ] Configure proper IAM roles and permissions
- [ ] Enable audit logging
- [ ] Set up vulnerability scanning
- [ ] Configure backup and disaster recovery
## Performance Optimization
### Database Optimization
- Connection pooling with PgBouncer
- Read replicas for analytics
- Proper indexing strategy
- Query optimization
- Regular VACUUM and ANALYZE
### Caching Strategy
- Redis cluster for high availability
- Proper cache invalidation
- Cache warming strategies
- Memory usage monitoring
### Application Optimization
- Horizontal pod autoscaling
- CPU and memory limits
- Connection pooling
- Async processing for heavy operations
- CDN for static assets
## Backup and Recovery
### Automated Backups
```bash
#!/bin/bash
# Database backup script
pg_dump $DATABASE_URL | gzip > /backups/calendar_$(date +%Y%m%d_%H%M%S).sql.gz
# Redis backup
redis-cli --rdb /backups/redis_$(date +%Y%m%d_%H%M%S).rdb
# Rotate old backups (keep last 30 days)
find /backups -name "*.gz" -mtime +30 -delete
find /backups -name "*.rdb" -mtime +30 -delete
```
### Disaster Recovery Plan
1. Database restoration from backup
2. Redis data reconstruction from database
3. Application deployment verification
4. Health check validation
5. Traffic gradual restoration
This production deployment guide ensures your MCP Calendar Server runs reliably and securely in production environments.