Overview
This guide explains how to deploy 16 WordPress sites to DigitalOcean App Platform with isolated MySQL databases.
Architecture
- MySQL Cluster: 1 cluster with 16 isolated databases ($15/month)
- Spaces: 1 bucket for media uploads ($5/month)
- App Platform: 16 apps ($80/month)
- Total Cost: $100/month
- Revenue: $192/month (16 sites × $12)
- Profit: $92/month
Prerequisites
✅ MySQL cluster created (e482d1d5-dd84-4329-b7c4-aa809c9be4bc) ✅ Database credentials saved (exports/do-deployment/database_credentials.jsonl) ✅ WordPress sites migrated to GitHub (14-15 of 16 sites) ✅ doctl authenticated
Deployment Steps
Step 1: Create Spaces Access Keys (Manual)
App Platform deployment requires Spaces access keys for media uploads. These must be created manually:
- Go to: https://cloud.digitalocean.com/account/api/spaces
- Click "Generate New Key"
- Name: wordpress-media-uploads
- Copy both the Access Key and Secret Key
- Update .env file:
DO_SPACES_KEY=your_access_key_here
DO_SPACES_SECRET=your_secret_key_here
IMPORTANT: Save these keys immediately! The secret key is only shown once.
Step 2: Run App Platform Deployment Script
cd /home/cw/Documents/IBG_HUB/rmm-psa-backend
./migration-scripts/deploy_app_platform.sh
The script will:
- ✅ Create Spaces bucket ibg-wordpress-media in syd1 region
- ✅ Prompt for confirmation (shows costs)
- ✅ Deploy 16 App Platform apps
- ✅ Configure environment variables for each app:
- DB_HOST, DB_PORT, DB_NAME, DB_USER, DB_PASSWORD
- WORDPRESS_DB_HOST, WORDPRESS_DB_NAME, WORDPRESS_DB_USER, WORDPRESS_DB_PASSWORD
- SPACES_BUCKET, SPACES_REGION, SPACES_ACCESS_KEY, SPACES_SECRET_KEY
- ✅ Generate app spec files in exports/do-deployment/app-deployments/
- ✅ Save deployment info to deployed_apps.jsonl
Estimated Time: 10-15 minutes (with rate limiting between deployments)
Step 3: Import Database Dumps
After apps are deployed and running:
./migration-scripts/import_databases.sh
The script will:
- ✅ Download database.sql from each GitHub repo
- ✅ Import to corresponding MySQL database
- ✅ Clean up temporary files
Requirements: MySQL client installed (sudo apt install mysql-client)
Step 4: Install WP Offload Media Plugin
For each site, install and configure the WP Offload Media Lite plugin:
- SSH into each app or use WP-CLI
- Install plugin: wp plugin install amazon-s3-and-cloudfront --activate
- Configure plugin with Spaces credentials:
- Provider: DigitalOcean Spaces
- Access Key: (from DO_SPACES_KEY)
- Secret Key: (from DO_SPACES_SECRET)
- Bucket: ibg-wordpress-media
- Region: syd1
- Path Prefix: sitename/ (e.g., collegeo/, murbahmowers/)
Step 5: Configure Custom Domains
For each app in the DigitalOcean dashboard:
- Go to App Platform → Select app → Settings → Domains
- Add custom domain (e.g., collegeo.com.au)
- Update DNS records:
- CNAME: www → app-url.ondigitalocean.app
- A: @ → (provided IP address)
- Wait for SSL certificate auto-provisioning
Deployment Details
App Specs
Each app uses this configuration:
- Instance Size: basic-xxs ($5/month)
- Region: syd (Sydney, Australia)
- Git Source: Independent-Business-Group/wordpress-{username}
- Branch: main
- Auto Deploy: On push
- Health Check: HTTP GET /
Environment Variables
All sites have isolated database credentials:
DB_HOST: wordpress-mysql-cluster-do-user-28531160-0.i.db.ondigitalocean.com
DB_PORT: 25060
DB_NAME: {username}_wp
DB_USER: {username}_user
DB_PASSWORD: {unique_password}
Security Model
- Each site has a separate MySQL user
- Each user can ONLY access their own database
- MySQL GRANT permissions enforce isolation
- If one site is compromised, other 15 databases remain secure
Troubleshooting
App Deployment Fails
# Check app logs
doctl apps logs <app-id> --type=run
# View app details
doctl apps get <app-id>
# Redeploy
doctl apps create-deployment <app-id>
Database Import Fails
# Test MySQL connection
mysql -h wordpress-mysql-cluster-do-user-28531160-0.i.db.ondigitalocean.com \
-P 25060 -u {username}_user -p{password} {database}_wp
# Show tables
mysql -h ... -u ... -p... {database}_wp -e "SHOW TABLES;"
Spaces Upload Fails
- Verify Spaces keys in .env
- Check plugin configuration
- Test with WP-CLI: wp media regenerate --yes
Monitoring
Check App Status
# List all apps
doctl apps list
# Get specific app
doctl apps get <app-id>
# View deployments
doctl apps list-deployments <app-id>
Check Database Usage
# Connect to cluster
mysql -h wordpress-mysql-cluster-do-user-28531160-0.i.db.ondigitalocean.com \
-P 25060 -u doadmin -p
# Show all databases
SHOW DATABASES;
# Check table sizes
SELECT table_schema AS 'Database',
ROUND(SUM(data_length + index_length) / 1024 / 1024, 2) AS 'Size (MB)'
FROM information_schema.tables
GROUP BY table_schema;
Check Spaces Usage
# List spaces
doctl spaces ls
# List bucket contents
doctl spaces ls-objects ibg-wordpress-media --region syd1
Costs Summary
| Service | Quantity | Unit Cost | Total |
| MySQL Cluster | 1 | $15/month | $15 |
| Spaces | 1 | $5/month | $5 |
| App Platform | 16 | $5/month | $80 |
| Total | | | $100/month |
| Revenue | 16 sites × $12 | $192/month | | Profit | | $92/month |
Next Steps After Deployment
- ✅ Verify all 16 apps are running
- ✅ Confirm database imports successful
- ✅ Install and configure WP Offload Media on all sites
- ✅ Test image uploads to Spaces
- ✅ Configure custom domains
- ✅ Update DNS records
- ✅ Verify SSL certificates
- ✅ Test site functionality
- ✅ Update PostgreSQL domains table with GitHub repos
- ✅ Import remaining WHMCS data (products, invoices, tickets)
Support
For issues: