# PVE Cloud Backup Self-hosted Proxmox VE backup manager for uploads to an existing rclone remote such as OneDrive. The application is intentionally small: - FastAPI backend and worker - Vue 3 + TypeScript + Tailwind frontend, compiled once into `static/` - SQLite for all runtime configuration and state - `pvesh` for Proxmox API access - `rclone copyto`, `rclone lsjson`, `rclone deletefile`, and `rclone rmdir` for exact remote objects and empty per-backup folders - systemd services, no Docker ## Layout All application files live under: ```text /opt/pve-cloud-backup/ backend/ frontend/ static/ data/ app.db logs/ scripts/ ``` The install script copies the two systemd service files to `/etc/systemd/system/`. ## Prerequisites Install these on the Proxmox host: - Python 3 with `venv` - Node.js and npm for the one-time frontend build - `rclone` - a preconfigured rclone remote, for example OneDrive or a crypt remote backed by OneDrive - working `pvesh` access as the service user The app does not configure rclone. If no remote exists, setup fails until you configure rclone yourself. ## Install From the installation directory: ```bash cd /opt/pve-cloud-backup ./scripts/install.sh ``` The installer: 1. creates required subdirectories, 2. creates a Python virtual environment, 3. installs Python dependencies, 4. builds the Vue frontend into `/opt/pve-cloud-backup/static`, 5. initializes SQLite migrations, 6. installs and starts systemd services. Open: ```text http://:8080 ``` ## Setup On first launch, complete the setup wizard: - Proxmox node - Proxmox backup storage - local backup directory - rclone executable path - rclone remote name - remote backup path/folder within that remote - Discord webhook URL - allowed CORS origins - default compression - default backup mode - default retention policy - max concurrent backups All settings are stored in SQLite at `/opt/pve-cloud-backup/data/app.db`. ## Recovery behavior On worker startup, unfinished records in these states are reconciled: - `pve_running` - `local_ready` - `uploading` - `remote_ready` - `local_deleting` - `deleting` The worker does not delete unknown local files or remote objects. Local archives are deleted only after upload finishes and the remote object is verified. ## Retention cleanup Retention cleanup runs in two places: - immediately after a backup completes for that job; - periodically from the worker service, once per hour, so changed rules and day-based expiry are enforced even if no new backup runs. Remote deletion uses exact-object `rclone deletefile` calls for backup records already known in SQLite, then best-effort `rclone rmdir` cleanup for the empty per-backup folder. ## Testing Run backend tests: ```bash cd /opt/pve-cloud-backup/backend ./.venv/bin/pytest ``` Run frontend type check and build: ```bash cd /opt/pve-cloud-backup/frontend npm run build ``` Manual integration test against a real Proxmox guest: ```bash INTEGRATION_VMID= API_URL=http://127.0.0.1:8080 /opt/pve-cloud-backup/scripts/integration-test.sh ``` Optional environment variables: - `INTEGRATION_GUEST_NAME` to assert the discovered guest name before queueing. - `INTEGRATION_STORAGE` to override the configured Proxmox backup storage. - `INTEGRATION_LOCAL_BACKUP_DIR` to assert the configured local dump directory. - `INTEGRATION_CRON` to choose the created job schedule. Default: `0 3 * * *`. - `INTEGRATION_RUN_NOW=0` to create the job without immediately queueing it. The script creates a disabled backup job and, by default, queues it once. Only run it when you intentionally want a real Proxmox backup and rclone upload. ## CI Gitea Actions workflow: ```text .gitea/workflows/ci.yml ``` It runs on pushes and pull requests to `main` and can also be started manually. The workflow checks: - backend dependency install and pytest; - frontend dependency install and production build; - shell script syntax. Package caches are enabled through the Gitea Actions cache-compatible setup actions: - Python/pip cache keyed by `backend/requirements.txt`; - npm cache keyed by `frontend/package-lock.json`. The Gitea runner must have action cache support configured for cache restore/save to work. ## Updates After pulling new code, apply it with: ```bash cd /opt/pve-cloud-backup git pull --ff-only ./scripts/apply-update.sh ``` For a stricter update that runs backend tests before restarting services: ```bash RUN_TESTS=1 ./scripts/apply-update.sh ``` The update script backs up `data/app.db`, rebuilds dependencies/assets, runs migrations, installs service units, restarts services, and checks API health. ## Uninstall ```bash /opt/pve-cloud-backup/scripts/uninstall.sh ``` The uninstall script removes and disables systemd services. It leaves `/opt/pve-cloud-backup` intact so `app.db`, logs, and any remaining data are not destroyed accidentally.