15 KiB
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
- Prerequisites
- Complete Fresh Deployment
- Docker Build & Startup
- Database Initialization
- Static Files & Migrations
- Verification Steps
- Troubleshooting
Prerequisites
Required on Production Server
# Check these are installed and running
docker --version # Docker 20.10+
docker compose --version # Docker Compose 2.0+
Required Locally (Before Deployment)
- All code changes committed to git (
mainbranch) - All missing files added to git and pushed
- Production environment variables backed up locally
.envs/.production/directory exists with proper credentials
Complete Fresh Deployment
Step 1: Save Production Environment Variables Locally
On your local development machine:
# 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:
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:
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:
cd ~
# Remove everything
rm -rf smoothschedule
Step 5: Pull Fresh Code from Git
On production server:
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:
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:
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:
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:
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:
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:
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:
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:
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:
cd ~/smoothschedule/smoothschedule
docker compose -f docker-compose.production.yml exec django python manage.py shell
Then in the Django shell:
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
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:
# 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)
# 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=<random-slug>/
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)
POSTGRES_HOST=postgres
POSTGRES_PORT=5432
POSTGRES_DB=smoothschedule
POSTGRES_USER=<random-username>
POSTGRES_PASSWORD=<random-secure-password>
Troubleshooting
Django Container Won't Start
Symptom: docker compose ps shows Django as exited
Solution:
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:
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:
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.comto the correct IP
Traefik Certificate Issues
Symptom: SSL certificate errors, HTTP redirects failing
Solution:
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:
# 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
# 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 <command>
# 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:
# 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 <previous-commit-hash>
# 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
-
Always backup before major changes:
- Database:
pg_dump - Environment variables: Copy
.envs/.production/locally - Code: Git commits
- Database:
-
Never modify code on production directly:
- Change code locally → Git push → Production git pull
- Only edit
.envfiles directly on production
-
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
-
Docker Compose File:
- Always use
-f docker-compose.production.ymlfor production - Never use
-f docker-compose.local.ymlon production
- Always use
-
Persistence:
- Database:
production_postgres_datavolume - Redis:
production_redis_datavolume - Traefik certs:
production_traefikvolume - These are preserved when using
down(notdown -v)
- Database:
Last Updated: December 2025 Deployment Version: Manual v1.0