deployment-guide

Production deployment guidance for Quart applications including Docker, Hypercorn configuration, environment management, monitoring, and performance tuning. Activates when deploying or optimizing for production.

allowed_tools: Read, Write

$ 安裝

git clone https://github.com/gizix/cc_projects /tmp/cc_projects && cp -r /tmp/cc_projects/quart-template/.claude/skills/deployment-guide ~/.claude/skills/cc_projects

// tip: Run this command in your terminal to install the skill


name: deployment-guide description: Production deployment guidance for Quart applications including Docker, Hypercorn configuration, environment management, monitoring, and performance tuning. Activates when deploying or optimizing for production. allowed-tools: Read, Write

Provide production deployment best practices and configurations for Quart applications.

Docker Configuration

Multi-Stage Dockerfile

# Build stage
FROM python:3.11-slim as builder

WORKDIR /app

# Install uv
RUN pip install uv

# Copy dependency files
COPY pyproject.toml ./

# Install dependencies
RUN uv pip install --system --no-cache .

# Runtime stage
FROM python:3.11-slim

WORKDIR /app

# Copy dependencies from builder
COPY --from=builder /usr/local/lib/python3.11/site-packages /usr/local/lib/python3.11/site-packages
COPY --from=builder /usr/local/bin /usr/local/bin

# Copy application code
COPY src/ ./src/

# Create non-root user
RUN useradd --create-home appuser && \
    chown -R appuser:appuser /app

USER appuser

# Expose port
EXPOSE 8000

# Run with Hypercorn
CMD ["hypercorn", "src.app:app", "--bind", "0.0.0.0:8000", "--workers", "4", "--worker-class", "asyncio"]

docker-compose.yml

version: '3.8'

services:
  web:
    build: .
    ports:
      - "8000:8000"
    environment:
      - DATABASE_URL=postgresql+asyncpg://postgres:password@db:5432/quart_db
      - QUART_ENV=production
      - SECRET_KEY=${SECRET_KEY}
      - JWT_SECRET_KEY=${JWT_SECRET_KEY}
    depends_on:
      - db
    restart: unless-stopped

  db:
    image: postgres:15-alpine
    environment:
      - POSTGRES_DB=quart_db
      - POSTGRES_PASSWORD=password
    volumes:
      - postgres_data:/var/lib/postgresql/data
    restart: unless-stopped

volumes:
  postgres_data:

Hypercorn Production Configuration

# Recommended production settings
hypercorn src.app:create_app() \
    --bind 0.0.0.0:8000 \
    --workers 4 \
    --worker-class asyncio \
    --access-log - \
    --error-log - \
    --access-logformat '%(h)s %(r)s %(s)s %(b)s %(D)s' \
    --graceful-timeout 30 \
    --keep-alive 5 \
    --backlog 100

Environment Variables

# Production .env (never commit!)
SECRET_KEY=generate-with-secrets.token-hex-32
JWT_SECRET_KEY=generate-with-secrets.token-hex-32
DATABASE_URL=postgresql+asyncpg://user:pass@host:5432/db
QUART_ENV=production
CORS_ORIGINS=https://example.com,https://app.example.com
LOG_LEVEL=INFO

Nginx Reverse Proxy

upstream quart_app {
    server 127.0.0.1:8000;
}

server {
    listen 80;
    server_name example.com;
    return 301 https://$server_name$request_uri;
}

server {
    listen 443 ssl http2;
    server_name example.com;

    ssl_certificate /path/to/cert.pem;
    ssl_certificate_key /path/to/key.pem;

    # Security headers
    add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always;
    add_header X-Content-Type-Options "nosniff" always;
    add_header X-Frame-Options "DENY" always;

    # WebSocket support
    location /ws {
        proxy_pass http://quart_app;
        proxy_http_version 1.1;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection "upgrade";
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_read_timeout 86400;
    }

    # HTTP requests
    location / {
        proxy_pass http://quart_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;
    }
}

Monitoring & Logging

import logging
from quart.logging import default_handler

# Configure logging
logging.basicConfig(
    level=logging.INFO,
    format='%(asctime)s - %(name)s - %(levelname)s - %(message)s'
)

# Add request logging
@app.before_request
async def log_request():
    app.logger.info(f'{request.method} {request.path}')

@app.after_request
async def log_response(response):
    app.logger.info(f'{request.method} {request.path} - {response.status_code}')
    return response

Health Check Endpoint

@app.route('/health')
async def health_check():
    """Health check endpoint for load balancers."""
    # Check database
    try:
        async with get_session() as session:
            await session.execute('SELECT 1')
        db_status = 'healthy'
    except Exception:
        db_status = 'unhealthy'

    return {
        'status': 'healthy' if db_status == 'healthy' else 'degraded',
        'database': db_status,
        'version': app.config.get('API_VERSION')
    }

Performance Tuning

  • Workers: (CPU cores * 2) + 1
  • Database pool: 5-10 per worker
  • Connection timeout: 30s
  • Keep-alive: 5s
  • Backlog: 100-1000

Pre-Deployment Checklist

  • DEBUG=False
  • Strong SECRET_KEY set
  • Explicit CORS origins
  • HTTPS enforced
  • Database migrations applied
  • Static files served by CDN/nginx
  • Logging configured
  • Health checks implemented
  • Monitoring setup
  • Backups configured
  • Rate limiting enabled
  • Security headers set