mirror of
https://github.com/pacnpal/thrillwiki_django_no_react.git
synced 2025-12-24 13:31:09 -05:00
- Introduced a comprehensive Secret Management Guide detailing best practices, secret classification, development setup, production management, rotation procedures, and emergency protocols. - Implemented a client-side performance monitoring script to track various metrics including page load performance, paint metrics, layout shifts, and memory usage. - Enhanced search accessibility with keyboard navigation support for search results, ensuring compliance with WCAG standards and improving user experience.
10 KiB
10 KiB
Deployment Guide
This guide covers deploying ThrillWiki to production environments, including configuration, security, and monitoring setup.
Pre-Deployment Checklist
Configuration
- All required environment variables set
DEBUG=False- Strong
SECRET_KEY(50+ characters, randomly generated) ALLOWED_HOSTSproperly configuredCSRF_TRUSTED_ORIGINSset- Database URL points to production database
- Redis URL configured for caching
Security
SECURE_SSL_REDIRECT=TrueSESSION_COOKIE_SECURE=TrueCSRF_COOKIE_SECURE=TrueSECURE_HSTS_SECONDSset (31536000 recommended)SECURE_HSTS_INCLUDE_SUBDOMAINS=True- No debug tools enabled
- Turnstile keys configured
Validation
Run the configuration validator:
python manage.py validate_settings --strict
python manage.py check --deploy
Environment Configuration
Required Environment Variables
# Core Django
SECRET_KEY=<generate-secure-key>
DEBUG=False
DJANGO_SETTINGS_MODULE=config.django.production
ALLOWED_HOSTS=thrillwiki.com,www.thrillwiki.com
CSRF_TRUSTED_ORIGINS=https://thrillwiki.com,https://www.thrillwiki.com
# Database
DATABASE_URL=postgis://user:pass@db-host:5432/thrillwiki
# Cache
REDIS_URL=redis://:password@redis-host:6379/1
# Email
EMAIL_BACKEND=django_forwardemail.backends.ForwardEmailBackend
FORWARD_EMAIL_API_KEY=your-api-key
FORWARD_EMAIL_DOMAIN=thrillwiki.com
# Security
TURNSTILE_SITE_KEY=your-site-key
TURNSTILE_SECRET_KEY=your-secret-key
SECURE_SSL_REDIRECT=True
SESSION_COOKIE_SECURE=True
CSRF_COOKIE_SECURE=True
# Cloudflare Images
CLOUDFLARE_IMAGES_ACCOUNT_ID=your-account-id
CLOUDFLARE_IMAGES_API_TOKEN=your-api-token
CLOUDFLARE_IMAGES_ACCOUNT_HASH=your-account-hash
# Road Trip Service
ROADTRIP_USER_AGENT=ThrillWiki/1.0 (https://thrillwiki.com)
# Frontend
FRONTEND_DOMAIN=https://thrillwiki.com
Optional Production Settings
# Monitoring
SENTRY_DSN=https://xxx@xxx.ingest.sentry.io/xxx
SENTRY_ENVIRONMENT=production
SENTRY_TRACES_SAMPLE_RATE=0.1
# Secret rotation
SECRET_ROTATION_ENABLED=True
SECRET_KEY_VERSION=1
# Performance tuning
DATABASE_CONN_MAX_AGE=600
REDIS_MAX_CONNECTIONS=100
Deployment Platforms
Docker Deployment
Create a production Dockerfile:
FROM python:3.11-slim
WORKDIR /app
# Install system dependencies
RUN apt-get update && apt-get install -y \
libpq-dev \
libgdal-dev \
libgeos-dev \
&& rm -rf /var/lib/apt/lists/*
# Install Python dependencies
COPY backend/pyproject.toml backend/uv.lock ./
RUN pip install uv && uv sync --frozen
# Copy application
COPY backend/ .
# Collect static files
RUN python manage.py collectstatic --noinput
# Run with Gunicorn
CMD ["gunicorn", "--bind", "0.0.0.0:8000", "--workers", "4", "thrillwiki.wsgi:application"]
Docker Compose for production:
version: '3.8'
services:
web:
build: .
environment:
- DJANGO_SETTINGS_MODULE=config.django.production
env_file:
- .env
ports:
- "8000:8000"
depends_on:
- db
- redis
db:
image: postgis/postgis:14-3.3
volumes:
- postgres_data:/var/lib/postgresql/data
environment:
- POSTGRES_DB=thrillwiki
- POSTGRES_USER=thrillwiki
- POSTGRES_PASSWORD=${DB_PASSWORD}
redis:
image: redis:7-alpine
command: redis-server --requirepass ${REDIS_PASSWORD}
volumes:
- redis_data:/data
celery:
build: .
command: celery -A config.celery worker -l info
env_file:
- .env
depends_on:
- db
- redis
celery-beat:
build: .
command: celery -A config.celery beat -l info
env_file:
- .env
depends_on:
- db
- redis
volumes:
postgres_data:
redis_data:
Heroku Deployment
# Create app
heroku create thrillwiki
# Add buildpacks
heroku buildpacks:add heroku/python
heroku buildpacks:add https://github.com/heroku/heroku-geo-buildpack.git
# Add add-ons
heroku addons:create heroku-postgresql:standard-0
heroku addons:create heroku-redis:premium-0
# Set environment variables
heroku config:set DJANGO_SETTINGS_MODULE=config.django.production
heroku config:set SECRET_KEY=$(python -c "from django.core.management.utils import get_random_secret_key; print(get_random_secret_key())")
heroku config:set DEBUG=False
# ... set other variables
# Deploy
git push heroku main
# Run migrations
heroku run python manage.py migrate
AWS ECS Deployment
Task definition example:
{
"family": "thrillwiki",
"containerDefinitions": [
{
"name": "web",
"image": "your-ecr-repo/thrillwiki:latest",
"portMappings": [
{
"containerPort": 8000,
"protocol": "tcp"
}
],
"environment": [
{
"name": "DJANGO_SETTINGS_MODULE",
"value": "config.django.production"
}
],
"secrets": [
{
"name": "SECRET_KEY",
"valueFrom": "arn:aws:secretsmanager:region:account:secret:thrillwiki/SECRET_KEY"
},
{
"name": "DATABASE_URL",
"valueFrom": "arn:aws:secretsmanager:region:account:secret:thrillwiki/DATABASE_URL"
}
]
}
]
}
Database Setup
PostgreSQL with PostGIS
-- Create database
CREATE DATABASE thrillwiki;
-- Enable PostGIS
\c thrillwiki
CREATE EXTENSION postgis;
CREATE EXTENSION postgis_topology;
-- Create user
CREATE USER thrillwiki_app WITH ENCRYPTED PASSWORD 'secure-password';
GRANT ALL PRIVILEGES ON DATABASE thrillwiki TO thrillwiki_app;
Run Migrations
python manage.py migrate
python manage.py createsuperuser
Static Files
Collect Static Files
python manage.py collectstatic --noinput
WhiteNoise Configuration
WhiteNoise is configured for production static file serving. No additional web server configuration needed for static files.
CDN Integration (Optional)
For CDN, update STATIC_URL:
STATIC_URL=https://cdn.thrillwiki.com/static/
Monitoring Setup
Sentry Integration
SENTRY_DSN=https://xxx@xxx.ingest.sentry.io/xxx
SENTRY_ENVIRONMENT=production
SENTRY_TRACES_SAMPLE_RATE=0.1
Health Checks
Health check endpoint: /health/
# Test health check
curl https://thrillwiki.com/health/
Configure load balancer health checks to use this endpoint.
Logging
Production logging outputs JSON to stdout (suitable for log aggregation):
{
"levelname": "INFO",
"asctime": "2024-01-15 10:30:00,000",
"module": "views",
"message": "Request processed"
}
Performance Tuning
Database Connection Pooling
DATABASE_CONN_MAX_AGE=600 # 10 minutes
DATABASE_CONNECT_TIMEOUT=10
DATABASE_STATEMENT_TIMEOUT=30000
Redis Connection Pooling
REDIS_MAX_CONNECTIONS=100
REDIS_CONNECTION_TIMEOUT=20
Gunicorn Configuration
# gunicorn.conf.py
workers = 4 # (2 * CPU cores) + 1
worker_class = "gevent"
worker_connections = 1000
timeout = 30
keepalive = 5
max_requests = 1000
max_requests_jitter = 50
Celery Workers
Start Workers
# Main worker
celery -A config.celery worker -l info
# Beat scheduler
celery -A config.celery beat -l info
# With specific queues
celery -A config.celery worker -l info -Q default,trending,analytics,cache
Supervisor Configuration
[program:celery]
command=/app/.venv/bin/celery -A config.celery worker -l info
directory=/app
user=www-data
numprocs=1
autostart=true
autorestart=true
stdout_logfile=/var/log/celery/worker.log
stderr_logfile=/var/log/celery/worker-error.log
[program:celery-beat]
command=/app/.venv/bin/celery -A config.celery beat -l info
directory=/app
user=www-data
numprocs=1
autostart=true
autorestart=true
stdout_logfile=/var/log/celery/beat.log
stderr_logfile=/var/log/celery/beat-error.log
SSL/TLS Configuration
Nginx Configuration
server {
listen 443 ssl http2;
server_name thrillwiki.com www.thrillwiki.com;
ssl_certificate /etc/letsencrypt/live/thrillwiki.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/thrillwiki.com/privkey.pem;
ssl_protocols TLSv1.2 TLSv1.3;
ssl_prefer_server_ciphers on;
ssl_ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256;
location / {
proxy_pass http://127.0.0.1:8000;
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;
}
}
server {
listen 80;
server_name thrillwiki.com www.thrillwiki.com;
return 301 https://$server_name$request_uri;
}
Post-Deployment Verification
Functional Tests
# API health
curl -s https://thrillwiki.com/api/health/ | jq
# Static files
curl -I https://thrillwiki.com/static/css/tailwind.css
# API endpoint
curl -s https://thrillwiki.com/api/v1/parks/ | jq
Security Headers
# Check security headers
curl -I https://thrillwiki.com/ | grep -E "(Strict-Transport|X-Frame|X-Content|Content-Security)"
Performance
# Response time
time curl -s https://thrillwiki.com/api/v1/parks/ > /dev/null
Rollback Procedure
If deployment fails:
-
Immediate rollback:
# Docker docker-compose down docker-compose -f docker-compose.previous.yml up -d # Heroku heroku rollback # Kubernetes kubectl rollout undo deployment/thrillwiki -
Database rollback (if needed):
python manage.py migrate app_name migration_number -
Investigate and fix:
- Check error logs
- Review configuration differences
- Test in staging environment
Maintenance
Regular Tasks
- Daily: Review error logs, check health endpoints
- Weekly: Review performance metrics, check disk usage
- Monthly: Rotate secrets, review security updates
- Quarterly: Full security audit, dependency updates
Backup Procedures
# Database backup
pg_dump thrillwiki > backup_$(date +%Y%m%d).sql
# Media files backup
aws s3 sync /app/media s3://thrillwiki-backups/media/