Sign inSign up

gfsolone/cpanelvault

By gfsolone

•Updated 4 days ago

Automated cPanel hosting backup downloader with FTP polling, resume support, and notifications

Image
0

1.0K

gfsolone/cpanelvault repository overview

⁠cPanelVault

A tool to automate full backups of cPanel-based shared hosting accounts. It triggers a full backup via the cPanel UAPI, waits for it to be ready, downloads it via FTP with automatic resume support, stores it locally, and removes the remote file when done.

README in italiano: README.it.md⁠

⁠Features

  • Full backup (fullbackup_to_homedir) via cPanel UAPI
  • FTP download with automatic resume on failure or interruption
  • Smart polling: waits until the remote file size stabilises before downloading
  • Automatic cleanup of expired local backups (configurable retention per host)
  • Multi-host: each hosting account has its own config and independent cron schedule
  • Web UI: live status dashboard with manual trigger button
  • CLI: backup, clean and serve commands
  • Notifications: Telegram, SMTP and Resend — all configurable from the JSON file
  • Docker-ready: one docker compose up and you're running

⁠Project structure

backup/
  config.py     — HostConfig dataclass, JSON loader
  cpanel.py     — cPanel UAPI backup request
  ftp.py        — connect, poll, resume download, remote delete
  cleaner.py    — local backup retention cleanup
  runner.py     — full orchestration for one host; writes status.json
  notify.py     — Telegram / SMTP / Resend notifications
web/
  app.py        — FastAPI app: dashboard, manual trigger, APScheduler
  templates/
    index.html  — status table, animated badges, run button
main.py         — CLI entry point
Dockerfile
docker-compose.yml
ftp_config_sample.json

⁠Configuration

Copy ftp_config_sample.json to ftp_config.json and fill in your credentials. This file is excluded from the repository via .gitignore.

{
    "notifications": { ... },
    "cpanel1": {
        "host": "example.com",
        "backup_local_dest_folder": "/backups",
        "cpanel_api_token": "YOUR_TOKEN",
        "cpanel_username": "admin",
        "ftp_password": "your-ftp-password",
        "ftp_username": "[email protected]",
        "mail_to_notify": "[email protected]",
        "time_to_wait": 60,
        "retention_days": 30,
        "schedule": "0 2 * * *"
    }
}
⁠Host fields
FieldTypeDefaultDescription
hoststring—FTP hostname (with or without ftp. prefix)
ftp_usernamestring—FTP username
ftp_passwordstring—FTP password
cpanel_usernamestring—cPanel username
cpanel_api_tokenstring—cPanel API token (see below)
backup_local_dest_folderstring—Local root folder for backups (/backups in Docker)
mail_to_notifystring—Email address cPanel uses to notify when the backup is ready
time_to_waitint60Seconds between size-stability checks during backup generation
retention_daysint30Local backup retention in days
schedulestring—Cron expression for the automatic scheduler (timezone set via TZ); omit for manual-only. Standard numeric weekdays are supported (0 and 7 = Sunday, 1 = Monday … 6 = Saturday)
request_after_downloadbooltrueWhen a pre-existing backup is found on the FTP server and downloaded, automatically request a fresh one via the cPanel API immediately after. Prevents a gap in backup history if the scheduler runs while an old file is still sitting on the server

The top-level key (cpanel1, website, etc.) is the name used in CLI commands and web UI URLs.

Backups are saved to backup_local_dest_folder/<hostname>/backup-*.tar.gz.

⁠Generating a cPanel API token
  1. Log in to cPanel → Manage API Tokens
  2. Create a new token with a descriptive name (e.g. backup-script)
  3. Paste the value into cpanel_api_token

⁠Notifications

All notification channels are configured inside the notifications key in ftp_config.json. You can enable multiple channels at the same time — every enabled channel receives the message.

⁠Telegram

Create a bot via @BotFather⁠ to get the token. Use @userinfobot⁠ or the channel/group ID (prefixed with -100) for chat_id.

"notifications": {
    "telegram": {
        "enabled": true,
        "bot_token": "123456789:AABBcc...",
        "chat_id": "-100123456789"
    }
}
⁠SMTP

Works with any SMTP server. For Gmail, use an App Password⁠ with port: 587 and use_ssl: false (STARTTLS). For native SSL use port: 465 and use_ssl: true.

"smtp": {
    "enabled": true,
    "host": "smtp.gmail.com",
    "port": 587,
    "use_ssl": false,
    "username": "[email protected]",
    "password": "app-password",
    "from": "cPanelVault <[email protected]>",
    "to": "[email protected]"
}
⁠Resend

Cloud email alternative. Sign up at resend.com⁠, verify your sender domain and create an API key.

"resend": {
    "enabled": true,
    "api_key": "re_xxxx...",
    "from": "cPanelVault <[email protected]>",
    "to": "[email protected]"
}

⁠Usage

Official multi-arch images (linux/amd64, linux/arm64) are published on every release:

RegistryImage
Docker Hubgfsolone/cpanelvault
GitHub Container Registryghcr.io/gioxx/cpanelvault

Tags: latest (newest release), X.Y.Z (a specific release), dev (current main, unreleased).

cp ftp_config_sample.json ftp_config.json
# edit ftp_config.json with your credentials
docker compose up -d

The web UI is available at http://localhost:8080. The bundled docker-compose.yml uses gfsolone/cpanelvault:latest; to build from source instead, replace image: with build: ..

Backups land in the Docker volume backups. To save them to a fixed path on the host machine, edit docker-compose.yml:

volumes:
  - ./ftp_config.json:/app/ftp_config.json:ro
  - /mnt/my-external-drive:/backups    # local path
  - data:/data
⁠Portainer

With Portainer you don't have a local project folder, so the config file must be placed on the Docker host manually before deploying the stack.

Step 1 — create the config file on the host (SSH into the machine running Docker):

mkdir -p /opt/cpanelvault
cp /path/to/ftp_config_sample.json /opt/cpanelvault/ftp_config.json
nano /opt/cpanelvault/ftp_config.json   # fill in your credentials

Step 2 — create a new stack in Portainer (Stacks → Add stack → Web editor) and paste:

services:
  cpanelvault:
    image: gfsolone/cpanelvault:latest
    ports:
      - "8080:8080"
    volumes:
      - /opt/cpanelvault/ftp_config.json:/app/ftp_config.json:ro
      - cpanelvault_backups:/backups
      - cpanelvault_data:/data
    environment:
      STATUS_FILE: /data/status.json
      CONFIG_FILE: /app/ftp_config.json
    restart: unless-stopped

volumes:
  cpanelvault_backups:
  cpanelvault_data:

The named volumes (cpanelvault_backups, cpanelvault_data) are created automatically and visible in Portainer under Volumes. If you want backups on a specific host path, replace the named volume with a bind mount:

    volumes:
      - /opt/cpanelvault/ftp_config.json:/app/ftp_config.json:ro
      - /mnt/my-external-drive:/backups
      - cpanelvault_data:/data

To update to a new release: Stacks → cpanelvault → Update the stack, with Re-pull image enabled. Pin a specific tag (e.g. gfsolone/cpanelvault:2.3.0) instead of latest if you prefer to upgrade manually.

⁠Local
pip install -r requirements.txt
cp ftp_config_sample.json ftp_config.json
# edit ftp_config.json

# Start web UI + automatic scheduler
python main.py serve

# Manual backup for a single host
python main.py backup cpanel1

# Backup all hosts sequentially
python main.py backup --all

# Preview expired backup cleanup (no deletion)
python main.py clean cpanel1 --dry-run

# Clean expired backups for all hosts
python main.py clean --all
⁠REST API

When the web UI is running the following endpoints are available:

MethodPathDescription
GET/HTML dashboard
GET/api/statusJSON status for all hosts
GET/api/hostsList of configured hosts
GET/backupsLocal archives page: volume usage, retention, expiry and removal dates
GET/api/backupsSame data as JSON
POST/backup/<name>Trigger backup in background
# Trigger from a shell script
curl -X POST http://localhost:8080/backup/cpanel1

# JSON status
curl http://localhost:8080/api/status

⁠Notes

  • The remote backup file is deleted only after the local download succeeds.
  • If a backup file already exists on the remote FTP from a previous session, it is downloaded directly without requesting a new one.
  • The scheduler runs on UTC; adjust cron expressions accordingly.
  • All logs go to stdout; with Docker use docker compose logs -f.
  • While a backup runs, the dashboard shows the current phase (checking FTP, waiting for cPanel, stability check, downloading, retention), download progress and a collapsible live log; after the run the panel keeps the last run's log. The page refreshes every 5s while a backup is running, 30s otherwise.
  • The Backups page lists the archives on the backup volume per host, with size, cPanel timestamp, download date, expiry (download date + retention_days) and the scheduled run that will remove them. Cleanup only happens at the end of a successful backup, so an expired archive stays until the next run.
  • Successful GET /, GET /api/status and GET /static/* requests (dashboard auto-refresh, Docker healthcheck) are not written to the access log. Set QUIET_ACCESS_LOG=false to log every request.

Tag summary

Content type

Image

Digest

sha256:fb5a82ee8…

Size

61.6 MB

Last updated

4 days ago

docker pull gfsolone/cpanelvault