Production Deployment¶
Guide for deploying Dash CopilotKit Components applications to production environments.
Overview¶
This guide covers best practices for deploying Dash applications with CopilotKit components to various production environments including cloud platforms, containers, and traditional servers.
Pre-deployment Checklist¶
Security¶
- Store API keys in environment variables
- Enable HTTPS/SSL certificates
- Configure CORS settings appropriately
- Implement rate limiting
- Set up authentication if required
- Review and sanitize user inputs
Performance¶
- Optimize component loading
- Configure caching strategies
- Set up CDN for static assets
- Monitor memory usage
- Test with expected user load
Monitoring¶
- Set up application logging
- Configure error tracking
- Implement health checks
- Set up performance monitoring
- Configure alerts
Environment Configuration¶
Environment Variables¶
Create a .env file for local development and configure environment variables in production:
# API Keys
COPILOTKIT_PUBLIC_API_KEY=ck_pub_your_key_here
OPENAI_API_KEY=sk-your_openai_key_here
# Application Settings
DASH_DEBUG=False
DASH_HOST=0.0.0.0
DASH_PORT=8050
# Database (if using)
DATABASE_URL=postgresql://user:password@localhost:5432/dbname
# Security
SECRET_KEY=your-secret-key-here
ALLOWED_HOSTS=yourdomain.com,www.yourdomain.com
Production App Configuration¶
import os
from dash import Dash
import dash_bootstrap_components as dbc
import dash_copilotkit_components
# Production configuration
app = Dash(
__name__,
external_stylesheets=[dbc.themes.BOOTSTRAP],
suppress_callback_exceptions=True,
# Disable dev tools in production
dev_tools_hot_reload=False,
dev_tools_ui=False,
dev_tools_props_check=False
)
# Security headers
app.server.config.update(
SECRET_KEY=os.environ.get('SECRET_KEY', 'dev-key-change-in-production'),
SESSION_COOKIE_SECURE=True,
SESSION_COOKIE_HTTPONLY=True,
SESSION_COOKIE_SAMESITE='Lax'
)
# Configure component with environment variables
def create_copilot_component():
return dash_copilotkit_components.DashCopilotkitComponents(
id='production-copilot',
ui_type='chat',
public_api_key=os.environ.get('COPILOTKIT_PUBLIC_API_KEY'),
# Or use custom runtime
api_key=os.environ.get('OPENAI_API_KEY'),
runtime_url=os.environ.get('COPILOTKIT_RUNTIME_URL'),
instructions='You are a production AI assistant.'
)
if __name__ == '__main__':
app.run(
host=os.environ.get('DASH_HOST', '127.0.0.1'),
port=int(os.environ.get('DASH_PORT', 8050)),
debug=os.environ.get('DASH_DEBUG', 'False').lower() == 'true'
)
Cloud Platform Deployment¶
Heroku Deployment¶
- Create required files:
requirements.txt:
dash>=2.14.0
dash-bootstrap-components>=1.5.0
dash-copilotkit-components>=0.1.0
gunicorn>=21.2.0
python-dotenv>=1.0.0
Procfile:
runtime.txt:
- Update app.py for Heroku:
import os
from dash import Dash
app = Dash(__name__)
server = app.server # Expose Flask server for Gunicorn
# Your app layout and callbacks here
if __name__ == '__main__':
app.run(
host='0.0.0.0',
port=int(os.environ.get('PORT', 8050)),
debug=False
)
- Deploy to Heroku:
# Install Heroku CLI and login
heroku login
# Create Heroku app
heroku create your-app-name
# Set environment variables
heroku config:set COPILOTKIT_PUBLIC_API_KEY=your_key_here
# Deploy
git add .
git commit -m "Deploy to Heroku"
git push heroku main
AWS Elastic Beanstalk¶
- Create
application.py:
from app import app
# EB looks for 'application' variable
application = app.server
if __name__ == '__main__':
application.run(debug=False)
- Create
.ebextensions/python.config:
option_settings:
aws:elasticbeanstalk:container:python:
WSGIPath: application.py
aws:elasticbeanstalk:application:environment:
PYTHONPATH: "/var/app/current:$PYTHONPATH"
- Deploy:
# Install EB CLI
pip install awsebcli
# Initialize EB application
eb init
# Create environment and deploy
eb create production-env
eb deploy
Google Cloud Platform (Cloud Run)¶
- Create
Dockerfile:
FROM python:3.11-slim
WORKDIR /app
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt
COPY . .
EXPOSE 8080
CMD ["gunicorn", "--bind", "0.0.0.0:8080", "app:server"]
- Create
cloudbuild.yaml:
steps:
- name: 'gcr.io/cloud-builders/docker'
args: ['build', '-t', 'gcr.io/$PROJECT_ID/dash-copilotkit-app', '.']
- name: 'gcr.io/cloud-builders/docker'
args: ['push', 'gcr.io/$PROJECT_ID/dash-copilotkit-app']
- name: 'gcr.io/cloud-builders/gcloud'
args: ['run', 'deploy', 'dash-copilotkit-app',
'--image', 'gcr.io/$PROJECT_ID/dash-copilotkit-app',
'--platform', 'managed',
'--region', 'us-central1']
- Deploy:
# Build and deploy
gcloud builds submit --config cloudbuild.yaml
# Set environment variables
gcloud run services update dash-copilotkit-app \
--set-env-vars COPILOTKIT_PUBLIC_API_KEY=your_key_here
Docker Deployment¶
Basic Dockerfile¶
FROM python:3.11-slim
# Set working directory
WORKDIR /app
# Install system dependencies
RUN apt-get update && apt-get install -y \
gcc \
&& rm -rf /var/lib/apt/lists/*
# Copy requirements and install Python dependencies
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt
# Copy application code
COPY . .
# Create non-root user
RUN useradd --create-home --shell /bin/bash app
USER app
# Expose port
EXPOSE 8050
# Health check
HEALTHCHECK --interval=30s --timeout=30s --start-period=5s --retries=3 \
CMD curl -f http://localhost:8050/_dash-layout || exit 1
# Run application
CMD ["gunicorn", "--bind", "0.0.0.0:8050", "--workers", "4", "app:server"]
Docker Compose for Development¶
version: '3.8'
services:
app:
build: .
ports:
- "8050:8050"
environment:
- COPILOTKIT_PUBLIC_API_KEY=${COPILOTKIT_PUBLIC_API_KEY}
- DASH_DEBUG=True
volumes:
- .:/app
depends_on:
- redis
- postgres
redis:
image: redis:7-alpine
ports:
- "6379:6379"
postgres:
image: postgres:15-alpine
environment:
- POSTGRES_DB=dashapp
- POSTGRES_USER=dashuser
- POSTGRES_PASSWORD=dashpass
ports:
- "5432:5432"
volumes:
- postgres_data:/var/lib/postgresql/data
volumes:
postgres_data:
Production Docker Compose¶
version: '3.8'
services:
app:
build: .
restart: unless-stopped
ports:
- "8050:8050"
environment:
- COPILOTKIT_PUBLIC_API_KEY=${COPILOTKIT_PUBLIC_API_KEY}
- DATABASE_URL=postgresql://dashuser:dashpass@postgres:5432/dashapp
- REDIS_URL=redis://redis:6379
depends_on:
- redis
- postgres
healthcheck:
test: ["CMD", "curl", "-f", "http://localhost:8050/_dash-layout"]
interval: 30s
timeout: 10s
retries: 3
nginx:
image: nginx:alpine
restart: unless-stopped
ports:
- "80:80"
- "443:443"
volumes:
- ./nginx.conf:/etc/nginx/nginx.conf
- ./ssl:/etc/nginx/ssl
depends_on:
- app
redis:
image: redis:7-alpine
restart: unless-stopped
volumes:
- redis_data:/data
postgres:
image: postgres:15-alpine
restart: unless-stopped
environment:
- POSTGRES_DB=dashapp
- POSTGRES_USER=dashuser
- POSTGRES_PASSWORD=dashpass
volumes:
- postgres_data:/var/lib/postgresql/data
volumes:
redis_data:
postgres_data:
Load Balancing and Scaling¶
Nginx Configuration¶
upstream dash_app {
server app1:8050;
server app2:8050;
server app3:8050;
}
server {
listen 80;
server_name yourdomain.com;
return 301 https://$server_name$request_uri;
}
server {
listen 443 ssl http2;
server_name yourdomain.com;
ssl_certificate /etc/nginx/ssl/cert.pem;
ssl_certificate_key /etc/nginx/ssl/key.pem;
location / {
proxy_pass http://dash_app;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
# WebSocket support
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
}
# Static files caching
location /assets/ {
expires 1y;
add_header Cache-Control "public, immutable";
}
}
Monitoring and Logging¶
Application Logging¶
import logging
import os
from pythonjsonlogger import jsonlogger
# Configure logging
log_level = os.environ.get('LOG_LEVEL', 'INFO')
logging.basicConfig(level=getattr(logging, log_level))
# JSON formatter for structured logging
logHandler = logging.StreamHandler()
formatter = jsonlogger.JsonFormatter()
logHandler.setFormatter(formatter)
logger = logging.getLogger()
logger.addHandler(logHandler)
# Log CopilotKit interactions
@app.callback(
Output('log-store', 'data'),
Input('copilot-component', 'value')
)
def log_copilot_interaction(value):
logger.info("CopilotKit interaction", extra={
'component_id': 'copilot-component',
'value_length': len(value) if value else 0,
'timestamp': datetime.utcnow().isoformat()
})
return {}
Health Check Endpoint¶
from flask import jsonify
import psutil
@app.server.route('/health')
def health_check():
try:
# Check system resources
cpu_percent = psutil.cpu_percent()
memory_percent = psutil.virtual_memory().percent
# Check external dependencies
# Add your checks here (database, APIs, etc.)
status = {
'status': 'healthy',
'timestamp': datetime.utcnow().isoformat(),
'system': {
'cpu_percent': cpu_percent,
'memory_percent': memory_percent
}
}
return jsonify(status), 200
except Exception as e:
return jsonify({
'status': 'unhealthy',
'error': str(e),
'timestamp': datetime.utcnow().isoformat()
}), 500
Security Best Practices¶
API Key Management¶
import os
from cryptography.fernet import Fernet
class SecureConfig:
def __init__(self):
self.encryption_key = os.environ.get('ENCRYPTION_KEY')
if self.encryption_key:
self.cipher = Fernet(self.encryption_key.encode())
def get_api_key(self):
encrypted_key = os.environ.get('ENCRYPTED_COPILOTKIT_KEY')
if encrypted_key and self.cipher:
return self.cipher.decrypt(encrypted_key.encode()).decode()
return os.environ.get('COPILOTKIT_PUBLIC_API_KEY')
config = SecureConfig()
Rate Limiting¶
from flask_limiter import Limiter
from flask_limiter.util import get_remote_address
limiter = Limiter(
app.server,
key_func=get_remote_address,
default_limits=["200 per day", "50 per hour"]
)
@app.server.route('/api/copilot')
@limiter.limit("10 per minute")
def copilot_api():
# Your API endpoint
pass
Performance Optimization¶
Caching¶
from flask_caching import Cache
cache = Cache(app.server, config={
'CACHE_TYPE': 'redis',
'CACHE_REDIS_URL': os.environ.get('REDIS_URL', 'redis://localhost:6379')
})
@cache.memoize(timeout=300)
def expensive_computation(params):
# Cache expensive operations
pass
Asset Optimization¶
# Compress assets
app.server.config['COMPRESS_MIMETYPES'] = [
'text/html', 'text/css', 'text/xml',
'application/json', 'application/javascript'
]
# Enable gzip compression
from flask_compress import Compress
Compress(app.server)
Troubleshooting¶
Common Issues¶
- Memory leaks: Monitor memory usage and implement proper cleanup
- Slow loading: Optimize component loading and use lazy loading
- API rate limits: Implement proper rate limiting and caching
- SSL certificate issues: Ensure proper certificate configuration
- Database connections: Use connection pooling and proper cleanup
Debugging in Production¶
# Enable debug mode safely
if os.environ.get('ENABLE_DEBUG') == 'true':
app.run(debug=True)
else:
# Production logging
import logging
logging.basicConfig(level=logging.INFO)
Next Steps¶
- Environment Configuration - Detailed environment setup
- Performance Optimization - Advanced performance tuning
- Monitoring Guide - Testing and monitoring strategies