# Staging Environment Setup Guide

**Version:** 1.0  
**Last Updated:** January 11, 2026  
**Purpose:** Complete staging environment setup for safe feature testing before production deployment

---

## Overview

This guide implements a **parallel staging environment** that allows you to:
- ✅ Test new features without affecting production users
- ✅ Run database migrations safely before production
- ✅ Validate UI/UX changes with real-world scenarios
- ✅ Test integrations (WordPress, payment providers, AI services)
- ✅ Merge to production only when staging is healthy

---

## Architecture: Production vs Staging

### Environment Comparison

| Component | Production | Staging |
|-----------|-----------|---------|
| **Domain** | `app.igny8.com` | `staging.igny8.com` |
| **API Domain** | `api.igny8.com` | `staging-api.igny8.com` |
| **Marketing** | `igny8.com` | `staging-marketing.igny8.com` |
| **Database** | `igny8_db` | `igny8_staging_db` |
| **Redis DB** | Redis DB 0 | Redis DB 1 |
| **Backend Port** | 8010 (internal) | 8012 (internal) |
| **Frontend Port** | 8021 (internal) | 8024 (internal) |
| **Marketing Port** | 8023 (internal) | 8026 (internal) |
| **Git Branch** | `main` | `staging` |
| **Compose File** | `docker-compose.app.yml` | `docker-compose.staging.yml` |
| **Container Prefix** | `igny8_` | `igny8_staging_` |
| **Project Name** | `igny8-app` | `igny8-staging` |
| **Env File** | `.env` | `.env.staging` |
| **Log Path** | `/data/app/logs/production/` | `/data/app/logs/staging/` |

### Shared Infrastructure

Both environments share (from `igny8-infra` stack):
- ✅ PostgreSQL server (different databases)
- ✅ Redis server (different DB indexes)
- ✅ Caddy reverse proxy (routes by domain)
- ✅ Docker network (`igny8_net`)
- ✅ Portainer (for management)

---

## File Structure

```
/data/app/igny8/
├── docker-compose.app.yml          # Production compose file
├── docker-compose.staging.yml      # Staging compose file (NEW)
├── .env                            # Production environment
├── .env.staging                    # Staging environment (NEW)
├── .env.example                    # Template for both
│
├── backend/
│   └── igny8_core/
│       ├── settings.py             # Detects DJANGO_ENV variable
│       └── ...
│
├── frontend/
│   └── src/
│       └── ...
│
├── scripts/
│   ├── deploy-production.sh       # Deploy production
│   ├── deploy-staging.sh          # Deploy staging (NEW)
│   ├── sync-data-to-staging.sh    # Sync prod DB → staging (NEW)
│   ├── rollback-production.sh     # Rollback script (NEW)
│   └── health-check.sh            # Check both environments (NEW)
│
└── logs/
    ├── production/
    │   ├── backend.log
    │   ├── celery-worker.log
    │   └── celery-beat.log
    └── staging/
        ├── backend.log
        ├── celery-worker.log
        └── celery-beat.log
```

---

## Step 1: Create Staging Database

### 1.1 Connect to PostgreSQL

```bash
docker exec -it postgres psql -U postgres
```

### 1.2 Create Staging Database

```sql
-- Create staging database
CREATE DATABASE igny8_staging_db OWNER igny8;

-- Grant privileges
GRANT ALL PRIVILEGES ON DATABASE igny8_staging_db TO igny8;

-- Exit
\q
```

### 1.3 Verify Database Creation

```bash
docker exec -it postgres psql -U igny8 -d igny8_staging_db -c "SELECT 'Staging DB Ready';"
```

---

## Step 2: Create Staging Compose File

Create `/data/app/igny8/docker-compose.staging.yml`:

```yaml
# =============================================================================
# IGNY8 STAGING ENVIRONMENT COMPOSE FILE
# =============================================================================
# This runs alongside production on the same server
# Uses different ports, database, and domains
# =============================================================================
#
# Usage:
#   docker compose -f docker-compose.staging.yml -p igny8-staging up -d
#   docker compose -f docker-compose.staging.yml -p igny8-staging down
#   docker compose -f docker-compose.staging.yml -p igny8-staging logs -f
# =============================================================================

name: igny8-staging

services:
  igny8_staging_backend:
    image: igny8-backend:staging
    container_name: igny8_staging_backend
    restart: always
    working_dir: /app
    ports:
      - "0.0.0.0:8012:8010"  # Different external port
    environment:
      # Environment identifier
      DJANGO_ENV: staging
      
      # Database (staging)
      DB_HOST: postgres
      DB_NAME: igny8_staging_db
      DB_USER: igny8
      DB_PASSWORD: igny8pass
      
      # Redis (staging - use DB 1 instead of 0)
      REDIS_HOST: redis
      REDIS_PORT: "6379"
      REDIS_DB: "1"
      
      # Security
      USE_SECURE_COOKIES: "True"
      USE_SECURE_PROXY_HEADER: "True"
      DEBUG: "False"
      
      # External services (can use same or separate keys)
      # OPENAI_API_KEY: set in .env.staging
      # STRIPE_SECRET_KEY: use test keys for staging
      # PAYPAL_CLIENT_ID: use sandbox for staging
      
    volumes:
      - /data/app/igny8/backend:/app:rw
      - /data/app/igny8:/data/app/igny8:rw
      - /var/run/docker.sock:/var/run/docker.sock:ro
      - /data/app/logs/staging:/app/logs:rw
    
    env_file:
      - .env.staging
    
    healthcheck:
      test: ["CMD-SHELL", "python -c \"import urllib.request; urllib.request.urlopen('http://localhost:8010/api/v1/system/status/').read()\" || exit 1"]
      interval: 30s
      timeout: 10s
      retries: 3
      start_period: 40s
    
    command: ["gunicorn", "igny8_core.wsgi:application", "--bind", "0.0.0.0:8010", "--workers", "4", "--timeout", "120"]
    
    networks: [igny8_net]
    
    labels:
      - "com.docker.compose.project=igny8-staging"
      - "com.docker.compose.service=igny8_staging_backend"

  igny8_staging_frontend:
    image: igny8-frontend-dev:staging
    container_name: igny8_staging_frontend
    restart: always
    ports:
      - "0.0.0.0:8024:5173"  # Different external port
    environment:
      VITE_BACKEND_URL: "https://staging-api.igny8.com/api"
      VITE_ENV: "staging"
    volumes:
      - /data/app/igny8/frontend:/app:rw
    depends_on:
      igny8_staging_backend:
        condition: service_healthy
    networks: [igny8_net]
    labels:
      - "com.docker.compose.project=igny8-staging"
      - "com.docker.compose.service=igny8_staging_frontend"

  igny8_staging_marketing_dev:
    image: igny8-marketing-dev:staging
    container_name: igny8_staging_marketing_dev
    restart: always
    ports:
      - "0.0.0.0:8026:5174"  # Different external port
    environment:
      VITE_BACKEND_URL: "https://staging-api.igny8.com/api"
      VITE_ENV: "staging"
    volumes:
      - /data/app/igny8/frontend:/app:rw
    networks: [igny8_net]
    labels:
      - "com.docker.compose.project=igny8-staging"
      - "com.docker.compose.service=igny8_staging_marketing_dev"

  igny8_staging_celery_worker:
    image: igny8-backend:staging
    container_name: igny8_staging_celery_worker
    restart: always
    working_dir: /app
    environment:
      DJANGO_ENV: staging
      DB_HOST: postgres
      DB_NAME: igny8_staging_db
      DB_USER: igny8
      DB_PASSWORD: igny8pass
      REDIS_HOST: redis
      REDIS_PORT: "6379"
      REDIS_DB: "1"
      C_FORCE_ROOT: "true"
    volumes:
      - /data/app/igny8/backend:/app:rw
      - /data/app/logs/staging:/app/logs:rw
    env_file:
      - .env.staging
    command: ["celery", "-A", "igny8_core", "worker", "--loglevel=info", "--concurrency=4"]
    depends_on:
      igny8_staging_backend:
        condition: service_healthy
    networks: [igny8_net]
    labels:
      - "com.docker.compose.project=igny8-staging"
      - "com.docker.compose.service=igny8_staging_celery_worker"

  igny8_staging_celery_beat:
    image: igny8-backend:staging
    container_name: igny8_staging_celery_beat
    restart: always
    working_dir: /app
    environment:
      DJANGO_ENV: staging
      DB_HOST: postgres
      DB_NAME: igny8_staging_db
      DB_USER: igny8
      DB_PASSWORD: igny8pass
      REDIS_HOST: redis
      REDIS_PORT: "6379"
      REDIS_DB: "1"
      C_FORCE_ROOT: "true"
    volumes:
      - /data/app/igny8/backend:/app:rw
      - /data/app/logs/staging:/app/logs:rw
    env_file:
      - .env.staging
    command: ["celery", "-A", "igny8_core", "beat", "--loglevel=info", "--scheduler", "django_celery_beat.schedulers:DatabaseScheduler"]
    depends_on:
      igny8_staging_backend:
        condition: service_healthy
    networks: [igny8_net]
    labels:
      - "com.docker.compose.project=igny8-staging"
      - "com.docker.compose.service=igny8_staging_celery_beat"

networks:
  igny8_net:
    external: true
```

---

## Step 3: Create Staging Environment File

Create `/data/app/igny8/.env.staging`:

```bash
# =============================================================================
# IGNY8 STAGING ENVIRONMENT CONFIGURATION
# =============================================================================

# Environment
DJANGO_ENV=staging
DEBUG=False

# Database
DB_HOST=postgres
DB_NAME=igny8_staging_db
DB_USER=igny8
DB_PASSWORD=igny8pass
DB_PORT=5432

# Redis
REDIS_HOST=redis
REDIS_PORT=6379
REDIS_DB=1

# Security
SECRET_KEY=staging-secret-key-change-this-in-production
ALLOWED_HOSTS=staging-api.igny8.com,staging.igny8.com,localhost
CORS_ALLOWED_ORIGINS=https://staging.igny8.com,https://staging-api.igny8.com

# API Keys - Use TEST/SANDBOX keys for staging
OPENAI_API_KEY=sk-test-your-openai-test-key
ANTHROPIC_API_KEY=sk-ant-test-your-anthropic-test-key

# Payment Gateways - Use SANDBOX/TEST mode
STRIPE_SECRET_KEY=sk_test_your_stripe_test_key
STRIPE_PUBLISHABLE_KEY=pk_test_your_stripe_test_key
STRIPE_WEBHOOK_SECRET=whsec_test_your_webhook_secret
PAYPAL_CLIENT_ID=sandbox_client_id
PAYPAL_CLIENT_SECRET=sandbox_client_secret
PAYPAL_MODE=sandbox

# Email - Use test email service
RESEND_API_KEY=re_test_your_resend_test_key
BREVO_API_KEY=test_your_brevo_test_key
DEFAULT_FROM_EMAIL=staging@igny8.com

# Image Generation - Use test/cheaper models if available
RUNWARE_API_KEY=test_key
BRIA_API_KEY=test_key

# Storage - Separate from production
MEDIA_ROOT=/app/media/staging
STATIC_ROOT=/app/staticfiles/staging

# URLs
FRONTEND_URL=https://staging.igny8.com
BACKEND_URL=https://staging-api.igny8.com
MARKETING_URL=https://staging-marketing.igny8.com

# Features (can enable/disable different features for testing)
ENABLE_LINKER=True
ENABLE_OPTIMIZER=True
ENABLE_SOCIALIZER=False

# Logging
LOG_LEVEL=DEBUG
```

---

## Step 4: Update Caddyfile for Staging Domains

Add to `/data/app/caddy/Caddyfile`:

```caddyfile
# =============================================================================
# STAGING ENVIRONMENT ROUTES
# =============================================================================

# Staging API Backend
staging-api.igny8.com {
    reverse_proxy igny8_staging_backend:8010 {
        header_up X-Real-IP {remote_host}
        header_up X-Forwarded-For {remote_host}
        header_up X-Forwarded-Proto {scheme}
    }
    
    # Enable WebSocket support
    @websockets {
        header Connection *Upgrade*
        header Upgrade websocket
    }
    reverse_proxy @websockets igny8_staging_backend:8010
    
    log {
        output file /data/logs/caddy/staging-api.access.log
    }
}

# Staging Frontend App
staging.igny8.com {
    reverse_proxy igny8_staging_frontend:5173 {
        header_up X-Real-IP {remote_host}
        header_up X-Forwarded-For {remote_host}
        header_up X-Forwarded-Proto {scheme}
    }
    
    # Vite HMR WebSocket support
    @websockets {
        header Connection *Upgrade*
        header Upgrade websocket
    }
    reverse_proxy @websockets igny8_staging_frontend:5173
    
    log {
        output file /data/logs/caddy/staging-app.access.log
    }
}

# Staging Marketing Site
staging-marketing.igny8.com {
    reverse_proxy igny8_staging_marketing_dev:5174 {
        header_up X-Real-IP {remote_host}
        header_up X-Forwarded-For {remote_host}
        header_up X-Forwarded-Proto {scheme}
    }
    
    # Vite HMR WebSocket support
    @websockets {
        header Connection *Upgrade*
        header Upgrade websocket
    }
    reverse_proxy @websockets igny8_staging_marketing_dev:5174
    
    log {
        output file /data/logs/caddy/staging-marketing.access.log
    }
}
```

Reload Caddy:
```bash
docker exec caddy caddy reload --config /etc/caddy/Caddyfile
```

---

## Step 5: DNS Configuration

Add these DNS records to your domain provider:

| Type | Name | Value | TTL |
|------|------|-------|-----|
| A | `staging` | `YOUR_SERVER_IP` | 3600 |
| A | `staging-api` | `YOUR_SERVER_IP` | 3600 |
| A | `staging-marketing` | `YOUR_SERVER_IP` | 3600 |

Or use CNAME if you prefer:

| Type | Name | Value | TTL |
|------|------|-------|-----|
| CNAME | `staging` | `igny8.com` | 3600 |
| CNAME | `staging-api` | `igny8.com` | 3600 |
| CNAME | `staging-marketing` | `igny8.com` | 3600 |

---

## Step 6: Git Branch Strategy

### 6.1 Create Staging Branch

```bash
cd /data/app/igny8
git checkout -b staging
git push -u origin staging
```

### 6.2 Branch Protection Rules (GitHub/GitLab)

**Main Branch:**
- ✅ Require pull request reviews
- ✅ Require status checks to pass (tests, linting)
- ✅ Require linear history
- ✅ Only allow merge from `staging` branch

**Staging Branch:**
- ✅ Require pull request reviews
- ✅ Allow merges from feature branches
- ✅ Auto-deploy to staging on push

### 6.3 Development Workflow

```
Feature Development:
───────────────────────────────────────
feature/new-ai-model → staging → main
feature/fix-bug      → staging → main
feature/ui-redesign  → staging → main

Process:
────────
1. Create feature branch from staging:
   git checkout staging
   git pull
   git checkout -b feature/my-new-feature

2. Develop & commit:
   git add .
   git commit -m "Add new feature"

3. Push & create PR to staging:
   git push -u origin feature/my-new-feature
   [Create PR: feature/my-new-feature → staging]

4. Deploy to staging:
   ./scripts/deploy-staging.sh

5. Test on staging environment

6. If healthy, merge staging → main:
   [Create PR: staging → main]
   [Review & Merge]

7. Deploy to production:
   ./scripts/deploy-production.sh
```

---

## Step 7: Create Deployment Scripts

### 7.1 Deploy Staging Script

Create `/data/app/igny8/scripts/deploy-staging.sh`:

```bash
#!/bin/bash
# =============================================================================
# Deploy Staging Environment
# =============================================================================

set -e  # Exit on error

echo "=========================================="
echo "IGNY8 Staging Deployment"
echo "=========================================="
echo ""

# Navigate to app directory
cd /data/app/igny8

# Checkout staging branch
echo "→ Checking out staging branch..."
git fetch origin
git checkout staging
git pull origin staging

# Build staging images with staging tag
echo ""
echo "→ Building staging images..."
docker build -t igny8-backend:staging -f backend/Dockerfile backend/
docker build -t igny8-frontend-dev:staging -f frontend/Dockerfile.dev frontend/
docker build -t igny8-marketing-dev:staging -f frontend/Dockerfile.marketing.dev frontend/

# Stop existing staging containers
echo ""
echo "→ Stopping existing staging containers..."
docker compose -f docker-compose.staging.yml -p igny8-staging down

# Start staging containers
echo ""
echo "→ Starting staging containers..."
docker compose -f docker-compose.staging.yml -p igny8-staging up -d

# Wait for backend to be healthy
echo ""
echo "→ Waiting for staging backend to be healthy..."
timeout 60 bash -c 'until docker exec igny8_staging_backend python -c "import urllib.request; urllib.request.urlopen(\"http://localhost:8010/api/v1/system/status/\").read()" 2>/dev/null; do sleep 2; done' || {
    echo "ERROR: Staging backend failed to start"
    docker compose -f docker-compose.staging.yml -p igny8-staging logs igny8_staging_backend
    exit 1
}

# Run migrations
echo ""
echo "→ Running database migrations..."
docker exec igny8_staging_backend python manage.py migrate --noinput

# Collect static files
echo ""
echo "→ Collecting static files..."
docker exec igny8_staging_backend python manage.py collectstatic --noinput

# Show container status
echo ""
echo "→ Container status:"
docker compose -f docker-compose.staging.yml -p igny8-staging ps

# Show health check
echo ""
echo "→ Health check:"
curl -s https://staging-api.igny8.com/api/v1/system/status/ | jq '.' || echo "Health check endpoint not responding"

echo ""
echo "=========================================="
echo "✅ Staging deployment complete!"
echo "=========================================="
echo ""
echo "Access staging environment:"
echo "  App:       https://staging.igny8.com"
echo "  API:       https://staging-api.igny8.com"
echo "  Marketing: https://staging-marketing.igny8.com"
echo ""
echo "View logs:"
echo "  docker compose -f docker-compose.staging.yml -p igny8-staging logs -f"
echo ""
```

Make executable:
```bash
chmod +x /data/app/igny8/scripts/deploy-staging.sh
```

---

### 7.2 Deploy Production Script

Create `/data/app/igny8/scripts/deploy-production.sh`:

```bash
#!/bin/bash
# =============================================================================
# Deploy Production Environment (with safety checks)
# =============================================================================

set -e  # Exit on error

echo "=========================================="
echo "IGNY8 Production Deployment"
echo "=========================================="
echo ""

# Confirmation prompt
read -p "⚠️  Deploy to PRODUCTION? This will affect live users. (yes/no): " confirm
if [[ "$confirm" != "yes" ]]; then
    echo "Deployment cancelled."
    exit 0
fi

# Navigate to app directory
cd /data/app/igny8

# Check current branch
current_branch=$(git branch --show-current)
if [[ "$current_branch" != "main" ]]; then
    echo "ERROR: Not on main branch (currently on: $current_branch)"
    echo "Switch to main branch first: git checkout main && git pull"
    exit 1
fi

# Pull latest from main
echo "→ Pulling latest from main branch..."
git pull origin main

# Create backup before deployment
echo ""
echo "→ Creating database backup..."
timestamp=$(date +%Y%m%d_%H%M%S)
docker exec postgres pg_dump -U igny8 igny8_db > /data/backups/igny8_db_before_deploy_${timestamp}.sql
echo "   Backup saved: /data/backups/igny8_db_before_deploy_${timestamp}.sql"

# Tag current production image for rollback
echo ""
echo "→ Tagging current production image for rollback..."
docker tag igny8-backend:latest igny8-backend:rollback || true
docker tag igny8-frontend-dev:latest igny8-frontend-dev:rollback || true

# Build new production images
echo ""
echo "→ Building production images..."
docker build -t igny8-backend:latest -f backend/Dockerfile backend/
docker build -t igny8-frontend-dev:latest -f frontend/Dockerfile.dev frontend/
docker build -t igny8-marketing-dev:latest -f frontend/Dockerfile.marketing.dev frontend/

# Run database migrations (check first)
echo ""
echo "→ Checking for pending migrations..."
pending=$(docker exec igny8_backend python manage.py showmigrations --plan | grep "\[ \]" || true)
if [[ -n "$pending" ]]; then
    echo "   Pending migrations found:"
    echo "$pending"
    read -p "   Apply migrations? (yes/no): " apply_migrations
    if [[ "$apply_migrations" == "yes" ]]; then
        docker exec igny8_backend python manage.py migrate --noinput
    fi
fi

# Restart production containers
echo ""
echo "→ Restarting production containers..."
docker compose -f docker-compose.app.yml -p igny8-app restart

# Wait for backend to be healthy
echo ""
echo "→ Waiting for production backend to be healthy..."
timeout 60 bash -c 'until docker exec igny8_backend python -c "import urllib.request; urllib.request.urlopen(\"http://localhost:8010/api/v1/system/status/\").read()" 2>/dev/null; do sleep 2; done' || {
    echo "ERROR: Production backend failed to start"
    echo "Consider rolling back: ./scripts/rollback-production.sh"
    exit 1
}

# Collect static files
echo ""
echo "→ Collecting static files..."
docker exec igny8_backend python manage.py collectstatic --noinput

# Show container status
echo ""
echo "→ Container status:"
docker compose -f docker-compose.app.yml -p igny8-app ps

# Show health check
echo ""
echo "→ Health check:"
curl -s https://api.igny8.com/api/v1/system/status/ | jq '.' || echo "Health check endpoint not responding"

echo ""
echo "=========================================="
echo "✅ Production deployment complete!"
echo "=========================================="
echo ""
echo "Monitor logs for 5 minutes:"
echo "  docker compose -f docker-compose.app.yml -p igny8-app logs -f"
echo ""
echo "If issues occur, rollback:"
echo "  ./scripts/rollback-production.sh"
echo ""
```

Make executable:
```bash
chmod +x /data/app/igny8/scripts/deploy-production.sh
```

---

### 7.3 Rollback Production Script

Create `/data/app/igny8/scripts/rollback-production.sh`:

```bash
#!/bin/bash
# =============================================================================
# Rollback Production to Previous Version
# =============================================================================

set -e  # Exit on error

echo "=========================================="
echo "IGNY8 Production Rollback"
echo "=========================================="
echo ""

# Confirmation prompt
read -p "⚠️  Rollback production to previous version? (yes/no): " confirm
if [[ "$confirm" != "yes" ]]; then
    echo "Rollback cancelled."
    exit 0
fi

cd /data/app/igny8

# Check if rollback images exist
if ! docker image inspect igny8-backend:rollback &> /dev/null; then
    echo "ERROR: No rollback image found. Cannot rollback."
    exit 1
fi

echo "→ Stopping current containers..."
docker compose -f docker-compose.app.yml -p igny8-app down

echo ""
echo "→ Restoring previous images..."
docker tag igny8-backend:rollback igny8-backend:latest
docker tag igny8-frontend-dev:rollback igny8-frontend-dev:latest

echo ""
echo "→ Starting containers with previous version..."
docker compose -f docker-compose.app.yml -p igny8-app up -d

# Wait for backend to be healthy
echo ""
echo "→ Waiting for backend to be healthy..."
timeout 60 bash -c 'until docker exec igny8_backend python -c "import urllib.request; urllib.request.urlopen(\"http://localhost:8010/api/v1/system/status/\").read()" 2>/dev/null; do sleep 2; done' || {
    echo "ERROR: Backend failed to start after rollback"
    exit 1
}

echo ""
echo "=========================================="
echo "✅ Rollback complete!"
echo "=========================================="
echo ""
echo "Check logs:"
echo "  docker compose -f docker-compose.app.yml -p igny8-app logs -f"
echo ""
```

Make executable:
```bash
chmod +x /data/app/igny8/scripts/rollback-production.sh
```

---

### 7.4 Sync Data to Staging Script

Create `/data/app/igny8/scripts/sync-data-to-staging.sh`:

```bash
#!/bin/bash
# =============================================================================
# Sync Production Data to Staging (sanitized)
# =============================================================================
# WARNING: This will REPLACE staging database with production data
# Use when you need to test with real-world data structure
# =============================================================================

set -e  # Exit on error

echo "=========================================="
echo "Sync Production → Staging"
echo "=========================================="
echo ""

# Confirmation prompt
read -p "⚠️  This will REPLACE staging database with production data. Continue? (yes/no): " confirm
if [[ "$confirm" != "yes" ]]; then
    echo "Sync cancelled."
    exit 0
fi

timestamp=$(date +%Y%m%d_%H%M%S)

echo "→ Dumping production database..."
docker exec postgres pg_dump -U igny8 igny8_db > /tmp/prod_dump_${timestamp}.sql

echo ""
echo "→ Dropping staging database..."
docker exec -i postgres psql -U postgres -c "DROP DATABASE IF EXISTS igny8_staging_db;"
docker exec -i postgres psql -U postgres -c "CREATE DATABASE igny8_staging_db OWNER igny8;"

echo ""
echo "→ Restoring to staging database..."
docker exec -i postgres psql -U igny8 -d igny8_staging_db < /tmp/prod_dump_${timestamp}.sql

echo ""
echo "→ Sanitizing staging data..."
docker exec igny8_staging_backend python manage.py shell << 'EOF'
from django.contrib.auth import get_user_model
from igny8_core.auth.models import Account

User = get_user_model()

# Sanitize emails (except admins)
for user in User.objects.filter(is_superuser=False):
    user.email = f"staging_{user.id}@igny8.test"
    user.save(update_fields=['email'])

print(f"✅ Sanitized {User.objects.filter(is_superuser=False).count()} user emails")

# Optional: Reset all passwords to a known staging password
# User.objects.filter(is_superuser=False).update(password='pbkdf2_sha256$...')

# Optional: Disable payment webhooks, external integrations
for account in Account.objects.all():
    # Clear sensitive API keys if stored
    pass

print("✅ Sanitization complete")
EOF

echo ""
echo "→ Running migrations (in case staging has newer migrations)..."
docker exec igny8_staging_backend python manage.py migrate --noinput

echo ""
echo "→ Cleaning up temp files..."
rm /tmp/prod_dump_${timestamp}.sql

echo ""
echo "=========================================="
echo "✅ Sync complete!"
echo "=========================================="
echo ""
echo "Staging database now contains sanitized production data."
echo "All user emails have been changed to staging_*@igny8.test"
echo ""
```

Make executable:
```bash
chmod +x /data/app/igny8/scripts/sync-data-to-staging.sh
```

---

### 7.5 Health Check Script

Create `/data/app/igny8/scripts/health-check.sh`:

```bash
#!/bin/bash
# =============================================================================
# Health Check for Both Environments
# =============================================================================

echo "=========================================="
echo "IGNY8 Health Check"
echo "=========================================="
echo ""

check_endpoint() {
    local name=$1
    local url=$2
    
    echo -n "Checking $name... "
    
    if response=$(curl -s -o /dev/null -w "%{http_code}" --max-time 10 "$url"); then
        if [[ "$response" == "200" ]]; then
            echo "✅ OK ($response)"
            return 0
        else
            echo "❌ FAIL (HTTP $response)"
            return 1
        fi
    else
        echo "❌ TIMEOUT"
        return 1
    fi
}

# Production checks
echo "PRODUCTION:"
check_endpoint "Backend API" "https://api.igny8.com/api/v1/system/status/"
check_endpoint "Frontend App" "https://app.igny8.com/"
check_endpoint "Marketing Site" "https://igny8.com/"

echo ""

# Staging checks
echo "STAGING:"
check_endpoint "Backend API" "https://staging-api.igny8.com/api/v1/system/status/"
check_endpoint "Frontend App" "https://staging.igny8.com/"
check_endpoint "Marketing Site" "https://staging-marketing.igny8.com/"

echo ""

# Container checks
echo "CONTAINERS:"
echo ""
echo "Production:"
docker compose -f /data/app/igny8/docker-compose.app.yml -p igny8-app ps --format "table {{.Name}}\t{{.Status}}\t{{.Ports}}"

echo ""
echo "Staging:"
docker compose -f /data/app/igny8/docker-compose.staging.yml -p igny8-staging ps --format "table {{.Name}}\t{{.Status}}\t{{.Ports}}"

echo ""
echo "=========================================="
```

Make executable:
```bash
chmod +x /data/app/igny8/scripts/health-check.sh
```

---

## Step 8: Initial Staging Deployment

### 8.1 Create Log Directories

```bash
mkdir -p /data/app/logs/staging
chmod -R 755 /data/app/logs/staging
```

### 8.2 Deploy Staging

```bash
cd /data/app/igny8
./scripts/deploy-staging.sh
```

### 8.3 Create Superuser for Staging

```bash
docker exec -it igny8_staging_backend python manage.py createsuperuser
```

### 8.4 Verify Staging

```bash
# Check health
./scripts/health-check.sh

# Access staging
echo "App:       https://staging.igny8.com"
echo "API:       https://staging-api.igny8.com"
echo "Marketing: https://staging-marketing.igny8.com"
```

---

## Testing Workflow

### Before Merging to Production

**1. Functional Testing:**
- ✅ Test all CRUD operations (Keywords, Clusters, Ideas, Tasks, Content)
- ✅ Test AI functions (clustering, content generation, image generation)
- ✅ Test automation pipeline (all 7 stages)
- ✅ Test WordPress publishing integration
- ✅ Test payment flows (using test/sandbox credentials)
- ✅ Test email notifications

**2. Migration Testing:**
```bash
# On staging
docker exec igny8_staging_backend python manage.py showmigrations
docker exec igny8_staging_backend python manage.py migrate --plan
docker exec igny8_staging_backend python manage.py migrate

# Check for errors
docker logs igny8_staging_backend
```

**3. Performance Testing:**
- ✅ Check response times for API endpoints
- ✅ Test with bulk operations (100+ keywords)
- ✅ Monitor Celery worker queue

**4. UI/UX Testing:**
- ✅ Test on different browsers (Chrome, Firefox, Safari)
- ✅ Test on mobile devices
- ✅ Check responsive design
- ✅ Verify all navigation flows

**5. Integration Testing:**
- ✅ WordPress plugin sync (use test WordPress site)
- ✅ Payment webhooks (Stripe test mode)
- ✅ Email delivery
- ✅ AI provider APIs (use test keys if available)

---

## Monitoring & Logs

### View Staging Logs

```bash
# All services
docker compose -f docker-compose.staging.yml -p igny8-staging logs -f

# Specific service
docker logs -f igny8_staging_backend
docker logs -f igny8_staging_frontend
docker logs -f igny8_staging_celery_worker

# Tail log files
tail -f /data/app/logs/staging/backend.log
tail -f /data/app/logs/staging/celery-worker.log
```

### View Production Logs

```bash
# All services
docker compose -f docker-compose.app.yml -p igny8-app logs -f

# Specific service
docker logs -f igny8_backend
docker logs -f igny8_frontend

# Tail log files
tail -f /data/app/logs/production/backend.log
```

---

## Maintenance Commands

### Restart Staging

```bash
docker compose -f docker-compose.staging.yml -p igny8-staging restart
```

### Stop Staging (save resources)

```bash
docker compose -f docker-compose.staging.yml -p igny8-staging down
```

### Rebuild Staging Images

```bash
docker build -t igny8-backend:staging -f backend/Dockerfile backend/
docker build -t igny8-frontend-dev:staging -f frontend/Dockerfile.dev frontend/
docker compose -f docker-compose.staging.yml -p igny8-staging up -d --force-recreate
```

### Clean Staging Database

```bash
docker exec -it postgres psql -U postgres -c "DROP DATABASE igny8_staging_db;"
docker exec -it postgres psql -U postgres -c "CREATE DATABASE igny8_staging_db OWNER igny8;"
docker exec igny8_staging_backend python manage.py migrate
```

---

## Best Practices

### 1. Always Test in Staging First

❌ **DON'T:**
```bash
git checkout main
git merge feature/new-feature
./scripts/deploy-production.sh  # DANGEROUS!
```

✅ **DO:**
```bash
git checkout staging
git merge feature/new-feature
./scripts/deploy-staging.sh
# Test thoroughly
# If healthy:
git checkout main
git merge staging
./scripts/deploy-production.sh
```

### 2. Use Test Credentials in Staging

- **Payment:** Stripe test mode, PayPal sandbox
- **Email:** Test SMTP or separate email service
- **AI APIs:** Separate API keys or same keys (monitor usage)
- **WordPress:** Test WordPress site, not production sites

### 3. Sanitize Data When Syncing

- Never expose real user emails in staging
- Replace sensitive data with dummy data
- Use `sync-data-to-staging.sh` which sanitizes automatically

### 4. Monitor Resource Usage

```bash
# Check container resources
docker stats igny8_staging_backend igny8_staging_celery_worker

# If staging uses too many resources:
docker compose -f docker-compose.staging.yml -p igny8-staging down
```

### 5. Keep Staging Up-to-Date

```bash
# Weekly: sync staging with main
git checkout staging
git merge main
git push origin staging
./scripts/deploy-staging.sh
```

---

## Troubleshooting

### Staging Backend Not Starting

```bash
# Check logs
docker logs igny8_staging_backend

# Common issues:
# 1. Database connection
docker exec -it postgres psql -U igny8 -d igny8_staging_db -c "SELECT 'OK';"

# 2. Redis connection
docker exec redis redis-cli -n 1 ping

# 3. Environment variables
docker exec igny8_staging_backend env | grep DB_
```

### SSL Certificate Issues

```bash
# Caddy automatically gets SSL certs
# Check Caddy logs
docker logs caddy

# Manually reload
docker exec caddy caddy reload --config /etc/caddy/Caddyfile
```

### Migration Conflicts

```bash
# If staging has migrations not in production:
# DO NOT merge to main until resolved

# Check diff
docker exec igny8_staging_backend python manage.py showmigrations
docker exec igny8_backend python manage.py showmigrations

# Resolve conflicts before merging
```

### Port Conflicts

```bash
# Check if staging ports are already in use
sudo netstat -tulpn | grep 8012
sudo netstat -tulpn | grep 8024

# If conflicts, change ports in docker-compose.staging.yml
```

---

## CI/CD Integration (Optional)

### GitHub Actions Example

Create `.github/workflows/deploy-staging.yml`:

```yaml
name: Deploy to Staging

on:
  push:
    branches: [staging]

jobs:
  deploy:
    runs-on: ubuntu-latest
    steps:
      - name: Deploy to staging server
        uses: appleboy/ssh-action@master
        with:
          host: ${{ secrets.SERVER_HOST }}
          username: ${{ secrets.SERVER_USER }}
          key: ${{ secrets.SSH_PRIVATE_KEY }}
          script: |
            cd /data/app/igny8
            ./scripts/deploy-staging.sh
```

### GitLab CI Example

Create `.gitlab-ci.yml`:

```yaml
stages:
  - deploy

deploy_staging:
  stage: deploy
  only:
    - staging
  script:
    - ssh user@server 'cd /data/app/igny8 && ./scripts/deploy-staging.sh'
  environment:
    name: staging
    url: https://staging.igny8.com
```

---

## Summary Checklist

### Initial Setup (One-Time)

- [ ] Create staging database (`igny8_staging_db`)
- [ ] Create `docker-compose.staging.yml`
- [ ] Create `.env.staging` with test credentials
- [ ] Update Caddyfile with staging domains
- [ ] Add staging DNS records
- [ ] Create `staging` git branch
- [ ] Create deployment scripts
- [ ] Create log directories
- [ ] Deploy staging for first time
- [ ] Create staging superuser
- [ ] Test staging health check

### Every Feature Deployment

- [ ] Develop in feature branch
- [ ] Merge feature → staging
- [ ] Deploy to staging: `./scripts/deploy-staging.sh`
- [ ] Test thoroughly on staging
- [ ] If healthy: Merge staging → main
- [ ] Deploy to production: `./scripts/deploy-production.sh`
- [ ] Monitor production for 10 minutes
- [ ] If issues: `./scripts/rollback-production.sh`

---

## Related Documentation

- [TWO-REPO-ARCHITECTURE.md](TWO-REPO-ARCHITECTURE.md) - Repository structure
- [INFRASTRUCTURE-STACK.md](INFRASTRUCTURE-STACK.md) - Stack setup
- [IGNY8-APP-STRUCTURE.md](IGNY8-APP-STRUCTURE.md) - App structure
- [DOCKER-DEPLOYMENT.md](DOCKER-DEPLOYMENT.md) - Docker details

---

**Document Maintainer:** IGNY8 DevOps Team  
**Last Review:** January 11, 2026  
**Next Review:** As needed when infrastructure changes
