# ThrillWiki Production Deployment Checklist

Use this checklist to verify your ThrillWiki deployment is production-ready.

> **Documentation References:**
> - [Environment Variables Reference](./configuration/environment-variables.md) - Complete variable documentation
> - [`.env.example`](../.env.example) - Example configuration file with production notes

## Pre-Deployment

### Configuration Validation

- [ ] Run `uv run manage.py validate_settings --strict` with no errors
- [ ] Run `uv run manage.py validate_state_machines` with no errors
- [ ] Run `uv run manage.py check --deploy` with no warnings
- [ ] All management commands execute without errors

### Environment Configuration

> **Important:** These variables are **required** for production. See [Environment Variables Reference](./configuration/environment-variables.md#production-required-variables) for detailed documentation.

- [ ] `DEBUG=False` is set (**security critical**)
- [ ] `SECRET_KEY` is unique, secure, and not committed to version control
- [ ] `ALLOWED_HOSTS` contains only production domains (no default in production)
- [ ] `CSRF_TRUSTED_ORIGINS` contains all production origins with `https://` prefix
- [ ] `DJANGO_SETTINGS_MODULE=config.django.production`
- [ ] `REDIS_URL` points to production Redis instance (**required for caching/sessions**)

### Database

- [ ] PostgreSQL with PostGIS is configured
- [ ] `DATABASE_URL` uses production credentials
- [ ] Database user has minimum required privileges
- [ ] Connection pooling is configured (`CONN_MAX_AGE=60`)
- [ ] Statement timeout is set (30 seconds recommended)
- [ ] Database backups are scheduled
- [ ] Point-in-time recovery is enabled (if required)

### Cache (Redis)

- [ ] `REDIS_URL` points to production Redis
- [ ] Redis is password protected (if exposed)
- [ ] Redis maxmemory policy is set (allkeys-lru recommended)
- [ ] Redis persistence is configured (if required)
- [ ] Separate Redis databases for cache, sessions, and API

### Security

- [ ] HTTPS is enforced (`SECURE_SSL_REDIRECT=True`)
- [ ] HSTS is enabled (`SECURE_HSTS_SECONDS=31536000`)
- [ ] Secure cookies enabled (`SESSION_COOKIE_SECURE=True`, `CSRF_COOKIE_SECURE=True`)
- [ ] X-Frame-Options set to DENY
- [ ] Content-Type-Nosniff enabled
- [ ] Referrer-Policy configured
- [ ] CORS properly configured (only trusted origins)
- [ ] Rate limiting is enabled
- [ ] SSL certificate is valid and auto-renewed

### Static Files

- [ ] `collectstatic` has been run
- [ ] Static files are served via CDN or nginx
- [ ] WhiteNoise is configured for compression
- [ ] Cache headers are set (long expiry for hashed files)

### Media Files

- [ ] Cloudflare Images credentials are configured
- [ ] Upload size limits are set
- [ ] File type validation is in place
- [ ] Cleanup tasks for orphaned images are scheduled

### Email

- [ ] Email backend is configured for production
- [ ] SPF, DKIM, and DMARC records are set
- [ ] Email sending is tested
- [ ] Admin email addresses are set

### Monitoring

- [ ] Sentry is configured (`SENTRY_DSN` set)
- [ ] Health check endpoints are accessible
- [ ] Logging is configured (JSON format recommended)
- [ ] Log rotation is configured
- [ ] Alerting is set up for errors

## Deployment

### Application

- [ ] All migrations are applied (`manage.py migrate`)
- [ ] Database schema matches expected state
- [ ] Application starts without errors
- [ ] Health check returns 200

### Container/Server

- [ ] Gunicorn is configured with appropriate workers
- [ ] Process manager (systemd/supervisor) is configured
- [ ] Container resource limits are set
- [ ] Restart policy is configured

### Reverse Proxy

- [ ] Nginx is configured correctly
- [ ] SSL termination is working
- [ ] HTMX headers are forwarded
- [ ] Static/media files are served correctly
- [ ] Gzip compression is enabled
- [ ] Security headers are set at proxy level

### Celery (if used)

- [ ] Celery worker is running
- [ ] Celery beat is running (for scheduled tasks)
- [ ] Task queues are configured
- [ ] Task monitoring is set up

## Post-Deployment Verification

### Functional Tests

- [ ] Homepage loads correctly
- [ ] Authentication works (login, logout)
- [ ] Park/ride pages load correctly
- [ ] Search functionality works
- [ ] HTMX interactions work
- [ ] Admin panel is accessible
- [ ] API endpoints respond correctly
- [ ] Image uploads work

### Cache Verification

- [ ] Run `uv run manage.py warm_cache` successfully
- [ ] Verify cache hit rate > 80% after warm-up
- [ ] Check Redis memory usage is within limits

### Performance Tests

- [ ] Page load time < 3 seconds
- [ ] API response time < 500ms
- [ ] No N+1 query issues (check Django Debug Toolbar/Silk)
- [ ] Cache hit rate > 80%
- [ ] Database query time reasonable

### Security Tests

- [ ] HTTPS redirect works
- [ ] HSTS header present
- [ ] Security headers present
- [ ] CORS blocks unauthorized origins
- [ ] Rate limiting triggers on excessive requests
- [ ] SQL injection attempts blocked
- [ ] XSS attempts blocked

### Monitoring Verification

- [ ] Sentry receives test error
- [ ] Health check endpoint monitored
- [ ] Logs are being collected
- [ ] Alerts trigger on test conditions

## Rollback Plan

### Preparation

- [ ] Previous version container/image available
- [ ] Database rollback script ready (if migrations changed schema)
- [ ] DNS rollback plan (if CDN change)
- [ ] Communication plan for stakeholders

### Rollback Steps

1. Stop current deployment
2. Restore previous container/code
3. Rollback database migrations (if needed)
4. Verify rollback with health check
5. Communicate status

## Emergency Contacts

| Role | Contact |
|------|---------|
| On-call Engineer | |
| Database Admin | |
| Infrastructure | |
| Product Owner | |

## Sign-off

| Role | Name | Date | Signature |
|------|------|------|-----------|
| Developer | | | |
| DevOps | | | |
| QA | | | |
| Tech Lead | | | |

---

## Quick Verification Commands

```bash
# Check Django configuration
uv run manage.py check --deploy

# Verify database connection
uv run manage.py dbshell -c "SELECT 1;"

# Test Redis connection
redis-cli -u $REDIS_URL ping

# Check health endpoint
curl -s https://yourdomain.com/api/v1/health/ | jq .status

# Verify static files
curl -I https://yourdomain.com/static/css/design-tokens.css

# Test HTTPS redirect
curl -I http://yourdomain.com

# Check security headers
curl -I https://yourdomain.com | grep -E "(Strict-Transport|X-Frame|X-Content)"
```

## Environment Variables Checklist

> **Complete Reference:** See [Environment Variables Reference](./configuration/environment-variables.md) for detailed documentation of all variables.

### Required (Production Will Fail Without These)

These variables have **no defaults** in production settings and must be explicitly set.
See [Production-Required Variables](./configuration/environment-variables.md#production-required-variables) for details.

```bash
# Security & Core (CRITICAL)
SECRET_KEY=<secure-random-key>          # Generate with: python -c "from django.core.management.utils import get_random_secret_key; print(get_random_secret_key())"
DEBUG=False                              # NEVER True in production
DJANGO_SETTINGS_MODULE=config.django.production

# Host Configuration (NO DEFAULTS IN PRODUCTION)
ALLOWED_HOSTS=yourdomain.com,www.yourdomain.com
CSRF_TRUSTED_ORIGINS=https://yourdomain.com,https://www.yourdomain.com  # MUST include https://

# Database & Cache (REQUIRED)
DATABASE_URL=postgis://user:pass@host:5432/dbname
REDIS_URL=redis://host:6379/0           # Required for caching, sessions, and Celery
```

### Recommended

```bash
SENTRY_DSN=https://xxx@sentry.io/xxx
CLOUDFLARE_IMAGES_ACCOUNT_ID=xxx
CLOUDFLARE_IMAGES_API_TOKEN=xxx
EMAIL_HOST=smtp.provider.com
EMAIL_HOST_USER=xxx
EMAIL_HOST_PASSWORD=xxx
```

### Optional

```bash
REDIS_SESSIONS_URL=redis://host:6379/1
REDIS_API_URL=redis://host:6379/2
SENTRY_TRACES_SAMPLE_RATE=0.1
```

### Validation Command

Run this to validate your production configuration:

```bash
DJANGO_SETTINGS_MODULE=config.django.production python manage.py check --deploy
```
