Files
itpp-infrastructure/docs/backup-restore/ARCHITECTURE.md
T

6.4 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 shown in UI)
  • Retention: 30 dayssnapshot.sh auto-deletes snapshots older than 30 days via cron
  • Average snapshot size: 16MB files + 74KB database
  • Total: ~1.4GB for full snapshot set
  • ⚠ Local only — not synced to S3. If app3 fails, all local snapshots are lost. Daily 3 AM S3 backup (app3-backup.sh) provides coarser off-site coverage.

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. The UI at my.itpropartner.com/backups/ is publicly accessible through Core's Caddy. Access control relies on obscurity (the domain is not widely known) and Caddy's TLS termination. For production use, consider adding IP whitelisting or Caddy basic auth.