- mkdocs.yml with dark slate theme, nav for 12 ITPP projects - build-docs.sh aggregates docs from all project repos - .gitea/workflows/docs-publish.yml for nightly rebuild+deploy - README and CHANGELOG for the itpp-docs repo itself - docs-source/ populated from all 12 repos - site/ ready for deployment to docs.itpropartner.com
6.0 KiB
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/<domain>/
v v |
[tar files] [render HTML] +------+------+
[mysqldump] [REST API] | | |
| | .tar.gz .sql note.txt
v v
/opt/backup-restore/ [Browser]
snapshots/<domain>/
<timestamp>/
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":"<timestamp>"}
| |
| 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 <snap>"}
| |
| 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:
/<domain>/<YYYY-MM-DD_HHMMSS>/ - 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:8090handle /api/restore→ app3:8090 (flush_interval -1, 300s read/write timeouts)handle /api/download/*→ app3:8090handle /api/log→ app3:8090handle_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
-
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).
-
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.
-
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.
-
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.