Codex 4a8da348d8
CI / Frontend build (push) Successful in 13s
CI / Backend tests (push) Successful in 17s
CI / Script syntax (push) Successful in 3s
Fixed timeout issues
2026-07-21 20:34:12 +02:00
2026-07-15 01:10:59 +02:00
2026-07-21 20:34:12 +02:00
2026-07-15 01:00:08 +02:00
2026-07-15 01:00:08 +02:00
2026-07-15 01:10:59 +02:00
2026-07-15 01:10:59 +02:00

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:

/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:

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:

http://<proxmox-host>: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:

cd /opt/pve-cloud-backup/backend
./.venv/bin/pytest

Run frontend type check and build:

cd /opt/pve-cloud-backup/frontend
npm run build

Manual integration test against a real Proxmox guest:

INTEGRATION_VMID=<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:

.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:

cd /opt/pve-cloud-backup
git pull --ff-only
./scripts/apply-update.sh

For a stricter update that runs backend tests before restarting services:

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

/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.

S
Description
No description provided
Readme 198 KiB
Languages
Python 65%
Vue 27.6%
Shell 6.4%
CSS 0.4%
HTML 0.2%
Other 0.4%