# Backup-Restore — Architecture ## Topology ``` INTERNET | [Caddy on Core] my.itpropartner.com | +---------------+---------------+ | | | /backups/* /api/restore /api/backup /api/log /api/download /api/delete | | | +-------+-------+-------+-------+ | app3 (152.53.241.111) netcup RS 4000 | [Flask :8090] /opt/backup-restore/ | +-------------------+-------------------+ | | | snapshot.sh app.py (UI+API) snapshots/ (cron 1AM,1PM) Jinja templates /opt/backup-restore/ | | snapshots// v v | [tar files] [render HTML] +------+------+ [mysqldump] [REST API] | | | | | .tar.gz .sql note.txt v v /opt/backup-restore/ [Browser] snapshots// / ``` ## Data Flow — Manual Backup ``` Browser (user clicks "Backup Now") | |-- POST /api/backup {"domain":"x.com","note":"pre-deploy"} | | | v | Caddy → app3:8090 | | | v | Flask api_backup() | | | |-- Parse nginx config → find htdocs path | |-- tar -czf files.tar.gz (timeout 300s) | |-- Parse wp-config.php → find DB_NAME | |-- mysqldump → database.sql (timeout 300s) | |-- Save note.txt, size.txt | |-- Return {"ok":true, "snapshot":""} | | | v | snapshots/x.com/2026-07-20_163208/ | files.tar.gz (16MB) | database.sql (74KB) | note.txt ("pre-deploy") | size.txt | v Browser reloads → new snapshot in list ``` ## Data Flow — Restore ``` Browser (user clicks Restore on a snapshot) | |-- POST /api/restore {"domain":"x.com","snapshot":"2026-07-20_130001"} | | | v | Caddy → app3:8090 (flush_interval -1, 300s timeouts) | | | v | Flask api_restore() | | | |-- Find snapshot path | |-- tar -xzf files.tar.gz → htdocs (timeout 300s) | |-- mysql < database.sql → WordPress DB (timeout 300s) | |-- chown -R site-user:site-user | |-- Log to restore.log: "TS|x.com|snap_id|OK" | |-- Return {"ok":true, "msg":"x.com restored to "} | | | v | Site is restored | v Browser shows success toast → Restore History updates ``` ## Components ### 1. Flask App (`/opt/backup-restore/app/app.py`) - Single-file Flask application, port 8090 - Jinja2 templating for backup dashboard (render_template_string) - 6 API endpoints (backup, restore, delete, download, log, index) - All HTML/CSS/JS inline in a single Python triple-quoted string - No auth — accessible via Caddy-only routing - Systemd: `backup-restore.service` ### 2. Snapshot Engine (`/opt/backup-restore/snapshot.sh`) - Bash script, runs at 1 AM and 1 PM via cron - Iterates all WordPress sites in `/etc/nginx/sites-enabled/` - Creates: files.tar.gz (document root), database.sql (MySQL dump) - Auto-cleanup: deletes snapshots older than 30 days - Log: `/opt/backup-restore/logs/snapshots.log` ### 3. Snapshot Storage (`/opt/backup-restore/snapshots/`) - Structure: `///` - 9 WordPress domains, 10 snapshots each (10 days retention shown) - Average snapshot size: 16MB files + 74KB database - Total: ~1.4GB for full snapshot set ### 4. Restore Log (`/opt/backup-restore/logs/restore.log`) - Pipe-delimited format: `timestamp|domain|snapshot_id|status` - Written by api_restore() on every restore attempt - Read by /api/log → displayed in Restore History table - Last 50 entries retained ### 5. Caddy Proxy (on Core) - `handle /api/backup` → app3:8090 - `handle /api/restore` → app3:8090 (flush_interval -1, 300s read/write timeouts) - `handle /api/download/*` → app3:8090 - `handle /api/log` → app3:8090 - `handle_path /backups/*` → app3:8090 (300s timeouts for long restores) - Domain: my.itpropartner.com ## 9 Hosted WordPress Sites All served by CloudPanel on app3, backed up by this system: | Domain | htdocs Path | DB Pattern | |---|---|---| | apextrackexperience.com | /home/apx/htdocs/apextrackexperience.com | wp-config DB_NAME | | boxpilotlogistics.com | /home/boxpilotlogistics/htdocs/boxpilotlogistics.com | wp-config DB_NAME | | debtrecoveryexperts.com | /home/debtrecoveryexperts/... | wp-config DB_NAME | | iamgmb.com | /home/iamgmb/... | wp-config DB_NAME | | katiewattdesign.com | /home/katiewattdesign/htdocs/katiewattdesign.com | wp-config DB_NAME | | katiewattsdesign.com | /home/katiewattsdesign/... | wp-config DB_NAME | | mainwp.itpropartner.com | /home/mainwp/... | wp-config DB_NAME | | vigilanttac.com | /home/vigilanttac/... | wp-config DB_NAME | | voipsimplicity.com | /home/voipsimplicity/... | wp-config DB_NAME | ## Key Design Decisions 1. **Single-file Flask app:** No package structure needed — the app has 6 endpoints and one HTML template. Keeping it in one file makes deployment trivial (scp + systemctl restart). 2. **Caddy on Core as single entry point:** app3 isn't exposed to the internet directly. All access goes through Core's Caddy with proper timeouts. The restore operation takes 30-45s and Caddy's default proxy timeout was killing connections mid-operation. 3. **Tar + mysqldump over rsync:** Snapshots are point-in-time archives, not incremental backups. Each snapshot is self-contained (files.tar.gz + database.sql). Restore is a single operation with no dependency chain. 4. **No auth on backup API:** The endpoints have no authentication. Access is controlled by Caddy routing — only requests through my.itpropartner.com reach the app. Internal network only.