# SmoothSchedule - Production Deployment Manual Complete step-by-step guide for manually deploying SmoothSchedule from scratch on a production server. This guide is useful when you need to reset the entire production deployment or troubleshoot deployment issues. ## Table of Contents 1. [Prerequisites](#prerequisites) 2. [Complete Fresh Deployment](#complete-fresh-deployment) 3. [Docker Build & Startup](#docker-build--startup) 4. [Database Initialization](#database-initialization) 5. [Static Files & Migrations](#static-files--migrations) 6. [Verification Steps](#verification-steps) 7. [Troubleshooting](#troubleshooting) --- ## Prerequisites ### Required on Production Server ```bash # Check these are installed and running docker --version # Docker 20.10+ docker compose --version # Docker Compose 2.0+ ``` ### Required Locally (Before Deployment) 1. All code changes committed to git (`main` branch) 2. All missing files added to git and pushed 3. Production environment variables backed up locally 4. `.envs/.production/` directory exists with proper credentials --- ## Complete Fresh Deployment ### Step 1: Save Production Environment Variables Locally **On your local development machine:** ```bash # Backup current production environment variables scp poduck@smoothschedule.com:~/smoothschedule/smoothschedule/.envs/.production/.django \ /tmp/production_django_env_backup scp poduck@smoothschedule.com:~/smoothschedule/smoothschedule/.envs/.production/.postgres \ /tmp/production_postgres_env_backup ``` ### Step 2: Prepare & Commit All Code Changes Locally **On your local development machine:** ```bash cd /home/poduck/Desktop/smoothschedule2 # Check for any missing files git status # Add all changes and new files git add -A # Review staged changes git diff --cached # Commit changes git commit -m "Production deployment: Add missing files and config updates" # Push to main branch git push origin main ``` ### Step 3: Bring Down Production Containers **SSH into production server:** ```bash ssh poduck@smoothschedule.com cd ~/smoothschedule/smoothschedule # Stop all containers (preserves volumes) docker compose -f docker-compose.production.yml down # Or to completely reset and remove volumes (careful!): docker compose -f docker-compose.production.yml down -v ``` ### Step 4: Remove Production Codebase **On production server:** ```bash cd ~ # Remove everything rm -rf smoothschedule ``` ### Step 5: Pull Fresh Code from Git **On production server:** ```bash cd ~ # Clone the repository git clone https://git.talova.net/poduck/smoothschedule.git smoothschedule cd smoothschedule ``` **Note:** If git authentication fails, configure credentials on the server: ```bash git config --global user.email "poduck@smoothschedule.com" git config --global user.name "Poduck" # Or store credentials git credential approve # Then paste: protocol=https # host=git.talova.net # username=poduck # password=chaff/starry ``` ### Step 6: Restore Production Environment Variables **On production server:** ```bash cd ~/smoothschedule/smoothschedule # Create the .envs/.production directory if it doesn't exist mkdir -p .envs/.production # Restore from backups on local machine or recreate them # Option A: Use SCP to copy from local machine scp /tmp/production_django_env_backup poduck@smoothschedule.com:~/smoothschedule/smoothschedule/.envs/.production/.django scp /tmp/production_postgres_env_backup poduck@smoothschedule.com:~/smoothschedule/smoothschedule/.envs/.production/.postgres # Option B: Manually recreate the files on the production server # (see Environment Variables section below) ``` --- ## Docker Build & Startup ### Step 7: Build Docker Images **On production server:** ```bash cd ~/smoothschedule/smoothschedule # Build all production Docker images docker compose -f docker-compose.production.yml build ``` **Expected output:** ``` ✓ Successfully built: - smoothschedule_production_django - smoothschedule_production_celeryworker - smoothschedule_production_celerybeat - smoothschedule_production_flower - smoothschedule_production_postgres - smoothschedule_production_traefik - smoothschedule-awscli ``` ### Step 8: Start Containers **On production server:** ```bash cd ~/smoothschedule/smoothschedule # Start all containers in detached mode docker compose -f docker-compose.production.yml up -d # Wait for services to stabilize (10-15 seconds) sleep 15 # Check container status docker compose -f docker-compose.production.yml ps ``` **Expected status:** All containers should be `Up` or `Healthy` --- ## Database Initialization ### Step 9: Run Database Migrations **On production server:** The application uses django-tenants for multi-tenant support. Migrations must be run in this order: ```bash cd ~/smoothschedule/smoothschedule # 1. Migrate the public/shared schema (all shared apps) docker compose -f docker-compose.production.yml exec -T django \ python manage.py migrate_schemas --shared # Expected output should show multiple migrations applied to "public" schema ``` **If you get "service django is not running":** The Django container may not be fully started. Wait a bit longer: ```bash sleep 30 # Wait for Django to initialize docker compose -f docker-compose.production.yml exec -T django \ python manage.py migrate_schemas --shared ``` --- ## Static Files & Migrations ### Step 10: Collect Static Files **On production server:** ```bash cd ~/smoothschedule/smoothschedule docker compose -f docker-compose.production.yml exec -T django \ python manage.py collectstatic --noinput ``` This collects all static files (CSS, JS, images) and uploads them to: - **Local filesystem:** `smoothschedule/staticfiles/` - **DigitalOcean Spaces/S3:** Configured via `DJANGO_AWS_*` environment variables ### Step 11: Create Superuser (First Time Only) **On production server, if this is a fresh installation:** ```bash cd ~/smoothschedule/smoothschedule docker compose -f docker-compose.production.yml exec django \ python manage.py createsuperuser # Follow the prompts to create the admin user ``` ### Step 12: Create Initial Tenant (First Time Only) **On production server, if you need a demo tenant:** ```bash cd ~/smoothschedule/smoothschedule docker compose -f docker-compose.production.yml exec django python manage.py shell ``` Then in the Django shell: ```python from core.models import Tenant, Domain from django.utils.text import slugify # Create a tenant tenant = Tenant.objects.create( name="Demo Company", slug="demo", is_free_trial=False, is_temporary=False, ) # Create a domain for the tenant Domain.objects.create( domain="demo.smoothschedule.com", tenant=tenant, ) print(f"Created tenant: {tenant.name} with domain: demo.smoothschedule.com") exit() ``` --- ## Verification Steps ### Step 13: Verify All Services Are Running ```bash cd ~/smoothschedule/smoothschedule # Check container status docker compose -f docker-compose.production.yml ps # View logs for any errors docker compose -f docker-compose.production.yml logs --tail=50 # Check specific service logs docker compose -f docker-compose.production.yml logs django --tail=30 docker compose -f docker-compose.production.yml logs postgres --tail=30 ``` ### Step 14: Test API Endpoints **From your local machine:** ```bash # Test the backend API curl -s "https://smoothschedule.com/api/health/" | jq # Test tenant access curl -s "https://demo.smoothschedule.com/api/resources/" | jq # Test platform admin curl -s "https://platform.smoothschedule.com/api/admin/businesses/" | jq ``` ### Step 15: Verify Nginx Frontend The frontend should be accessible at: - **Main site:** `https://smoothschedule.com` - **Platform dashboard:** `https://platform.smoothschedule.com` - **Tenant subdomain:** `https://demo.smoothschedule.com` --- ## Environment Variables Reference ### Django Configuration (`.envs/.production/.django`) ```bash # Security & Secrets DJANGO_SECRET_KEY=your-secret-key-here DJANGO_DEBUG=False DJANGO_ALLOWED_HOSTS=smoothschedule.com,platform.smoothschedule.com,*.smoothschedule.com # Admin & URLs DJANGO_ADMIN_URL=/ FRONTEND_URL=https://platform.smoothschedule.com PLATFORM_BASE_URL=https://platform.smoothschedule.com # Celery & Redis REDIS_URL=redis://redis:6379/0 CELERY_BROKER_URL=redis://redis:6379/0 # AWS/DigitalOcean Spaces (for media storage) DJANGO_AWS_ACCESS_KEY_ID=your-access-key DJANGO_AWS_SECRET_ACCESS_KEY=your-secret-key DJANGO_AWS_STORAGE_BUCKET_NAME=smoothschedule DJANGO_AWS_S3_ENDPOINT_URL=https://nyc3.digitaloceanspaces.com DJANGO_AWS_S3_REGION_NAME=nyc3 DJANGO_AWS_S3_CUSTOM_DOMAIN=smoothschedule.nyc3.digitaloceanspaces.com # Email Configuration (optional) MAILGUN_API_KEY=your-mailgun-key MAILGUN_DOMAIN=mg.smoothschedule.com # SSL/Security DJANGO_SECURE_SSL_REDIRECT=True DJANGO_SECURE_HSTS_SECONDS=60 DJANGO_SECURE_HSTS_INCLUDE_SUBDOMAINS=True DJANGO_SECURE_HSTS_PRELOAD=True DJANGO_SESSION_COOKIE_SECURE=True DJANGO_CSRF_COOKIE_SECURE=True # Sentry (optional - error tracking) SENTRY_DSN= SENTRY_ENVIRONMENT=production SENTRY_TRACES_SAMPLE_RATE=0.1 ``` ### PostgreSQL Configuration (`.envs/.production/.postgres`) ```bash POSTGRES_HOST=postgres POSTGRES_PORT=5432 POSTGRES_DB=smoothschedule POSTGRES_USER= POSTGRES_PASSWORD= ``` --- ## Troubleshooting ### Django Container Won't Start **Symptom:** `docker compose ps` shows Django as exited **Solution:** ```bash cd ~/smoothschedule/smoothschedule # View full error logs docker compose -f docker-compose.production.yml logs django --tail=100 # Check if database is accessible docker compose -f docker-compose.production.yml logs postgres --tail=20 # Restart Django after fixing the issue docker compose -f docker-compose.production.yml restart django ``` ### Migration Fails with "role does not exist" **Symptom:** Error when running `migrate_schemas --shared`: ``` FATAL: role "postgres" does not exist ``` **Solution:** The database volume is corrupted. Reset and restart: ```bash cd ~/smoothschedule/smoothschedule # Stop and remove all volumes docker compose -f docker-compose.production.yml down -v # Restart (this will recreate the database) docker compose -f docker-compose.production.yml up -d # Wait for database to initialize (30 seconds) sleep 30 # Try migrations again docker compose -f docker-compose.production.yml exec -T django \ python manage.py migrate_schemas --shared ``` ### Cannot Connect to Production Server **Check SSH access:** ```bash ssh poduck@smoothschedule.com "echo 'Connection successful'" ``` **If that fails:** - Verify your SSH key is authorized on the server - Check firewall rules allow SSH (port 22) - Verify DNS resolves `smoothschedule.com` to the correct IP ### Traefik Certificate Issues **Symptom:** SSL certificate errors, HTTP redirects failing **Solution:** ```bash cd ~/smoothschedule/smoothschedule # Check Traefik logs docker compose -f docker-compose.production.yml logs traefik --tail=50 # Restart Traefik to request new certificates docker compose -f docker-compose.production.yml restart traefik # Wait for Let's Encrypt to issue certificates (5-10 minutes) # Check via: https://smoothschedule.com (should show valid cert) ``` ### Frontend Not Loading **Verify nginx container is running:** ```bash # Check if nginx image was built docker images | grep smoothschedule # If missing, rebuild docker compose -f docker-compose.production.yml build nginx # Restart the service docker compose -f docker-compose.production.yml restart # Check logs docker logs $(docker ps -q --filter "name=nginx") ``` --- ## Quick Reference Commands ```bash # SSH to production server ssh poduck@smoothschedule.com # Navigate to project cd ~/smoothschedule/smoothschedule # View all container logs docker compose -f docker-compose.production.yml logs -f # View Django logs only docker compose -f docker-compose.production.yml logs django -f --tail=100 # Stop all containers docker compose -f docker-compose.production.yml down # Start all containers docker compose -f docker-compose.production.yml up -d # Restart a specific service docker compose -f docker-compose.production.yml restart django # Run a Django management command docker compose -f docker-compose.production.yml exec -T django python manage.py # Access Django shell docker compose -f docker-compose.production.yml exec django python manage.py shell # Create a database backup docker compose -f docker-compose.production.yml exec -T postgres \ pg_dump -U $POSTGRES_USER smoothschedule > backup.sql # Execute arbitrary SQL docker compose -f docker-compose.production.yml exec -T postgres \ psql -U $POSTGRES_USER smoothschedule -c "SELECT version();" # Check Docker resource usage docker stats ``` --- ## Advanced: Rollback Procedure If a deployment fails catastrophically: ```bash # 1. Stop everything cd ~/smoothschedule/smoothschedule docker compose -f docker-compose.production.yml down # 2. Restore from database backup (if available) docker compose -f docker-compose.production.yml exec -T postgres \ psql -U $POSTGRES_USER smoothschedule < backup.sql # 3. Checkout previous git commit cd ~/smoothschedule git checkout # 4. Rebuild and restart cd smoothschedule docker compose -f docker-compose.production.yml build docker compose -f docker-compose.production.yml up -d ``` --- ## Monitoring Checklist After deployment, monitor these for 24 hours: - [ ] No error logs in `docker compose logs django` - [ ] API endpoints respond with 200 status - [ ] SSL certificates are valid (https://smoothschedule.com) - [ ] Frontend loads without console errors - [ ] Tenant subdomains work correctly - [ ] Static files are being served (CSS, JS load) - [ ] Background tasks execute (check Celery worker logs) - [ ] No database connection errors --- ## Important Notes 1. **Always backup before major changes:** - Database: `pg_dump` - Environment variables: Copy `.envs/.production/` locally - Code: Git commits 2. **Never modify code on production directly:** - Change code locally → Git push → Production git pull - Only edit `.env` files directly on production 3. **Multi-Tenancy Considerations:** - Each tenant gets a separate PostgreSQL schema - Migrations apply to "public" schema (shared) first - Tenant migrations happen automatically on first request to new tenant 4. **Docker Compose File:** - Always use `-f docker-compose.production.yml` for production - Never use `-f docker-compose.local.yml` on production 5. **Persistence:** - Database: `production_postgres_data` volume - Redis: `production_redis_data` volume - Traefik certs: `production_traefik` volume - These are preserved when using `down` (not `down -v`) --- **Last Updated:** December 2025 **Deployment Version:** Manual v1.0