Files
smoothschedule/DEPLOYMENT.md

11 KiB

SmoothSchedule Production Deployment Guide

Prerequisites

Server Requirements

  • Ubuntu/Debian Linux server
  • Minimum 2GB RAM, 20GB disk space
  • Docker and Docker Compose installed
  • Domain name pointed to server IP: smoothschedule.com
  • DNS configured with wildcard subdomain: *.smoothschedule.com

Required Accounts/Services

  • DigitalOcean Spaces (already configured)
    • Access Key: DO801P4R8QXYMY4CE8WZ
    • Bucket: smoothschedule
    • Region: nyc3
  • Email service (optional - Mailgun or SMTP)
  • Sentry (optional - error tracking)

Pre-Deployment Checklist

1. DigitalOcean Spaces Setup

# Create the bucket (if not already created)
aws --profile do-tor1 s3 mb s3://smoothschedule

# Set bucket to public-read for static/media files
aws --profile do-tor1 s3api put-bucket-acl \
  --bucket smoothschedule \
  --acl public-read

# Configure CORS (for frontend uploads)
cat > cors.json <<EOF
{
  "CORSRules": [
    {
      "AllowedOrigins": ["https://smoothschedule.com", "https://*.smoothschedule.com"],
      "AllowedMethods": ["GET", "PUT", "POST", "DELETE", "HEAD"],
      "AllowedHeaders": ["*"],
      "MaxAgeSeconds": 3000
    }
  ]
}
EOF

aws --profile do-tor1 s3api put-bucket-cors \
  --bucket smoothschedule \
  --cors-configuration file://cors.json

2. DNS Configuration

Configure these DNS records at your domain registrar:

Type    Name                Value                TTL
A       smoothschedule.com  YOUR_SERVER_IP      300
A       *.smoothschedule.com YOUR_SERVER_IP     300
CNAME   www                 smoothschedule.com  300

3. Environment Variables Review

Backend (.envs/.production/.django):

  • DJANGO_SECRET_KEY - Set
  • DJANGO_ALLOWED_HOSTS - Set to .smoothschedule.com
  • DJANGO_AWS_ACCESS_KEY_ID - Set
  • DJANGO_AWS_SECRET_ACCESS_KEY - Set
  • DJANGO_AWS_STORAGE_BUCKET_NAME - Set to smoothschedule
  • DJANGO_AWS_S3_ENDPOINT_URL - Set to https://nyc3.digitaloceanspaces.com
  • DJANGO_AWS_S3_REGION_NAME - Set to nyc3
  • MAILGUN_API_KEY - Optional (for email)
  • MAILGUN_DOMAIN - Optional (for email)
  • SENTRY_DSN - Optional (for error tracking)

Frontend (.env.production):

  • VITE_API_URL - Set to https://smoothschedule.com/api

Deployment Steps

Step 1: Server Preparation

# SSH into production server
ssh poduck@smoothschedule.com

# Install Docker (if not already installed)
curl -fsSL https://get.docker.com -o get-docker.sh
sudo sh get-docker.sh
sudo usermod -aG docker $USER

# Install Docker Compose
sudo curl -L "https://github.com/docker/compose/releases/latest/download/docker-compose-$(uname -s)-$(uname -m)" -o /usr/local/bin/docker-compose
sudo chmod +x /usr/local/bin/docker-compose

# Logout and login again for group changes to take effect
exit
ssh poduck@smoothschedule.com

Step 2: Deploy Backend (Django)

# Create deployment directory
mkdir -p ~/smoothschedule
cd ~/smoothschedule

# Clone the repository (or upload files via rsync/git)
# Option A: Clone from Git
git clone <your-repo-url> .
git checkout main

# Option B: Copy from local machine
# From your local machine:
# rsync -avz --exclude 'node_modules' --exclude '.venv' --exclude '__pycache__' \
#   /home/poduck/Desktop/smoothschedule2/ poduck@smoothschedule.com:~/smoothschedule/

# Navigate to backend
cd smoothschedule

# Build and start containers
docker compose -f docker-compose.production.yml build
docker compose -f docker-compose.production.yml up -d

# Wait for containers to start
sleep 10

# Check logs
docker compose -f docker-compose.production.yml logs -f

Step 3: Database Initialization

# Run migrations
docker compose -f docker-compose.production.yml exec django python manage.py migrate

# Create public schema (for multi-tenancy)
docker compose -f docker-compose.production.yml exec django python manage.py migrate_schemas --shared

# Create superuser
docker compose -f docker-compose.production.yml exec django python manage.py createsuperuser

# Collect static files (uploads to DigitalOcean Spaces)
docker compose -f docker-compose.production.yml exec django python manage.py collectstatic --noinput

Step 4: Create Initial Tenant

# Access Django shell
docker compose -f docker-compose.production.yml exec django python manage.py shell

# In the shell, create your first business tenant:
from core.models import Business
from django.contrib.auth import get_user_model

User = get_user_model()

# Create a business
business = Business.objects.create(
    name="Demo Business",
    subdomain="demo",
    schema_name="demo",
)

# Verify it was created
print(f"Created business: {business.name} at {business.subdomain}.smoothschedule.com")

# Create a business owner
owner = User.objects.create_user(
    username="demo_owner",
    email="owner@demo.com",
    password="your_password_here",
    role="owner",
    business_subdomain="demo"
)

print(f"Created owner: {owner.username}")
exit()

Step 5: Deploy Frontend

# On your local machine
cd /home/poduck/Desktop/smoothschedule2/frontend

# Install dependencies
npm install

# Build for production
npm run build

# Upload build files to server
rsync -avz dist/ poduck@smoothschedule.com:~/smoothschedule-frontend/

# On the server, set up nginx or serve via backend

Option A: Serve via Django (simpler)

The Django collectstatic command already handles static files. For serving the frontend:

  1. Copy frontend build to Django static folder
  2. Django will serve it via Traefik

Option B: Separate Nginx (recommended for production)

# Install nginx
sudo apt-get update
sudo apt-get install -y nginx

# Create nginx config
sudo nano /etc/nginx/sites-available/smoothschedule
server {
    listen 80;
    server_name smoothschedule.com *.smoothschedule.com;

    # Frontend (React)
    location / {
        root /home/poduck/smoothschedule-frontend;
        try_files $uri $uri/ /index.html;
    }

    # Backend API (proxy to Traefik)
    location /api {
        proxy_pass http://localhost:80;
        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;
    }

    location /admin {
        proxy_pass http://localhost:80;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
    }
}
# Enable site
sudo ln -s /etc/nginx/sites-available/smoothschedule /etc/nginx/sites-enabled/
sudo nginx -t
sudo systemctl reload nginx

Step 6: SSL/HTTPS Setup

Traefik is configured to automatically obtain Let's Encrypt SSL certificates. Ensure:

  1. DNS is pointed to your server
  2. Ports 80 and 443 are accessible
  3. Wait for Traefik to obtain certificates (check logs)
# Monitor Traefik logs
docker compose -f docker-compose.production.yml logs -f traefik

# You should see:
# "Certificate obtained for domain smoothschedule.com"

Step 7: Verify Deployment

# Check all containers are running
docker compose -f docker-compose.production.yml ps

# Should show:
# - django (running)
# - postgres (running)
# - redis (running)
# - traefik (running)
# - celeryworker (running)
# - celerybeat (running)
# - flower (running)

# Test API endpoint
curl https://smoothschedule.com/api/

# Test admin
curl https://smoothschedule.com/admin/

# Access in browser:
# https://smoothschedule.com - Main site
# https://platform.smoothschedule.com - Platform dashboard
# https://demo.smoothschedule.com - Demo business
# https://smoothschedule.com:5555 - Flower (Celery monitoring)

Post-Deployment

1. Monitoring

# View logs
docker compose -f docker-compose.production.yml logs -f

# View specific service logs
docker compose -f docker-compose.production.yml logs -f django
docker compose -f docker-compose.production.yml logs -f postgres

# Monitor Celery tasks via Flower
# Access: https://smoothschedule.com:5555
# Login with credentials from .envs/.production/.django

2. Backups

# Database backup
docker compose -f docker-compose.production.yml exec postgres backup

# List backups
docker compose -f docker-compose.production.yml exec postgres backups

# Restore from backup
docker compose -f docker-compose.production.yml exec postgres restore backup_filename.sql.gz

3. Updates

# Pull latest code
cd ~/smoothschedule/smoothschedule
git pull origin main

# Rebuild and restart
docker compose -f docker-compose.production.yml build
docker compose -f docker-compose.production.yml up -d

# Run migrations
docker compose -f docker-compose.production.yml exec django python manage.py migrate

# Collect static files
docker compose -f docker-compose.production.yml exec django python manage.py collectstatic --noinput

Troubleshooting

SSL Certificate Issues

# Check Traefik logs
docker compose -f docker-compose.production.yml logs traefik

# Verify DNS is pointing to server
dig smoothschedule.com +short

# Ensure ports are open
sudo ufw allow 80
sudo ufw allow 443

Database Connection Issues

# Check PostgreSQL is running
docker compose -f docker-compose.production.yml ps postgres

# Check database logs
docker compose -f docker-compose.production.yml logs postgres

# Verify connection
docker compose -f docker-compose.production.yml exec django python manage.py dbshell

Static Files Not Loading

# Verify DigitalOcean Spaces credentials
docker compose -f docker-compose.production.yml exec django python manage.py shell
>>> from django.conf import settings
>>> print(settings.AWS_ACCESS_KEY_ID)
>>> print(settings.AWS_STORAGE_BUCKET_NAME)

# Re-collect static files
docker compose -f docker-compose.production.yml exec django python manage.py collectstatic --noinput

# Check Spaces bucket
aws --profile do-tor1 s3 ls s3://smoothschedule/static/
aws --profile do-tor1 s3 ls s3://smoothschedule/media/

Celery Not Running Tasks

# Check Celery worker logs
docker compose -f docker-compose.production.yml logs celeryworker

# Access Flower dashboard
# https://smoothschedule.com:5555

# Restart Celery
docker compose -f docker-compose.production.yml restart celeryworker celerybeat

Security Checklist

  • SSL/HTTPS enabled via Let's Encrypt
  • DJANGO_SECRET_KEY set to random value
  • Database password set to random value
  • Flower dashboard password protected
  • Firewall configured (UFW or iptables)
  • SSH key-based authentication enabled
  • Fail2ban installed for brute-force protection
  • Regular backups configured
  • Sentry error monitoring (optional)

Performance Optimization

  1. Enable CDN for DigitalOcean Spaces

    • In Spaces settings, enable CDN
    • Update DJANGO_AWS_S3_CUSTOM_DOMAIN=smoothschedule.nyc3.cdn.digitaloceanspaces.com
  2. Scale Gunicorn Workers

    • Adjust WEB_CONCURRENCY in .envs/.production/.django
    • Formula: (2 x CPU cores) + 1
  3. Add Redis Persistence

    • Update docker-compose.production.yml redis config
    • Enable AOF persistence
  4. Database Connection Pooling

    • Already configured via CONN_MAX_AGE=60

Maintenance

Weekly

  • Review error logs
  • Check disk space: df -h
  • Monitor Flower dashboard for failed tasks

Monthly

  • Update Docker images: docker compose pull
  • Update dependencies: uv sync
  • Review backups

As Needed

  • Scale resources (CPU/RAM)
  • Add more Celery workers
  • Optimize database queries