Custom Backup Script: Weekly Backups with Monthly Retention

Custom Backup Script: Weekly Backups with Monthly Retention

I’m sharing a custom backup script I use for my HestiaCP server. It creates weekly backups (4 per month) and automatically keeps only the most recent backup from previous months, helping save disk space while maintaining a good backup history.

Features:

  • Creates organized backups in date-structured directories (e.g., /backup/12 DEC 2025/)
  • Maintains symlinks in /backup/ for HestiaCP panel compatibility
  • Symlinks are essential - they allow HestiaCP to update backup counters correctly and enable users to download, restore, and delete backups from the panel, even though backups are organized in monthly folders
  • Automatically cleans orphaned backups
  • Optional: Keep only 1 backup per user from previous months (configurable)
  • HTML email notifications with full backup summary
  • Detailed logging for each backup session
  • Database integrity checks before backup
  • Robust cleanup of temporary files and incomplete backups

Installation:

  1. Create the script file:

    nano /usr/local/hestia/bin/v-backup-users-custom
    
  2. Paste the script content (I’ll provide it below)

  3. Set proper permissions:

    chmod 755 /usr/local/hestia/bin/v-backup-users-custom
    
  4. In HestiaCP Admin Panel:

    • Go to Settings → Backups
    • Ensure “Local backup” is enabled
    • Go to Cron Jobs
    • Find the existing backup cron job: 00 01 * * 0 sudo /usr/local/hestia/bin/v-backup-users
    • Change it to: 00 01 * * 0 sudo /usr/local/hestia/bin/v-backup-users-custom
    • Adjust the time as needed (current example: Sunday at 1:00 AM)

Configuration Options:

At the top of the script, you can customize:

# Auto-detect admin user with ROLE='admin' (leave empty)
ADMIN_USER_OVERRIDE=""

# Use CONTACT email from admin user (leave empty)
# Or set manually: ADMIN_EMAIL_OVERRIDE="[email protected]"
ADMIN_EMAIL_OVERRIDE=""

# Keep only 1 backup per user from previous months (TRUE/FALSE)
# When TRUE: Keeps only the most recent backup from previous months
# When FALSE: Keeps all backups (default HestiaCP behavior)
KEEP_ONLY_ONE_OLD_BACKUP="FALSE"

How it works:

  1. Runs weekly (or as configured in cron)
  2. Creates backups in organized folders: /backup/DD MMM YYYY/username/
  3. Maintains symlinks in /backup/ for panel compatibility
    • These symlinks are crucial - they allow HestiaCP to:
      • Update backup counters (U_BACKUPS) correctly
      • Display backups in the user panel
      • Enable download, restore, and delete functions
      • All functions work normally despite backups being organized in monthly folders
  4. If KEEP_ONLY_ONE_OLD_BACKUP="TRUE", automatically removes old backups from previous months, keeping only the latest one per user
  5. Sends HTML email summary with backup status
  6. Logs everything for troubleshooting

User Panel Compatibility:

Even though backups are stored in organized monthly folders, all HestiaCP panel functions work perfectly:

  • Users can see their backups in the panel
  • Download backups directly
  • Restore backups with one click
  • Delete backups from the panel
  • Backup counters are updated correctly

This is possible because the script maintains symlinks in the /backup/ directory that point to the actual backup files in the organized folders.

Remote Backup Support:

You can customize this script to work with remote backups (FTP, SFTP, B2, Rclone) by modifying the backup functions. The script is based on HestiaCP’s original v-backup-user logic but with enhanced organization and cleanup features.

**Script:
**

https://github.com/webxtekagency/hestiacp-useful-tools/tree/main/scripts/backup-users-custom

Notes:

  • This script replaces the standard v-backup-users for scheduled backups
  • You can still run manual backups using the original command
  • All backups remain compatible with HestiaCP’s restore functions
  • The script includes error handling and cleanup for interrupted backups
  • Symlinks ensure full panel functionality while maintaining organized storage

Feedback and improvements welcome

I have already custom for daily,weakly,monthly creating username_daily etc and replacing the old one! Good job and thanks for sharing! I can provide also the scripts if someone need.

It’s not a bad option, although I prefer not to modify how Hestia performs its backups.

It’s not much more complicated to create a bash script that handles backup cleanup.

For this, the bash script should read and edit the files in:
/usr/local/hestia/data/users
and delete the backups. It works very well.

I have the logic set up to maintain 7 daily backups + 3 weekly backups + 2 monthly backups, and I haven’t had any problems.

The difference is that by following my implementation you get a highly configurable script with a tenth the weight of your code.

Just sharing something i been using for years on vesta. :love_you_gesture:

Thanks for sharing!

@Blats and @Antonio if you have something that you think is better and are willing to share: please do :slight_smile:

I don’t use GitHub, and it’s not something so special that it needs to be shared.

in the file:
/usr/local/hestia/data/users/USER_XX/backup.conf
You have the complete list of all backups for each account.

and in:
/usr/local/hestia/data/users/USER_XX/user.conf
You have the stored backup counter U_BACKUPS=‘’

You just need to go through each user’s list, determine which backups you want to delete (by age), and update the backup counter (user.conf) and their backup list (backup.conf).

You can create your own backup schedule—daily, weekly, monthly, annual, whatever you want.
You don’t need to use anyone else’s.

An easy, simple, and extremely useful solution if you don’t have a minimum of knowledge of BASH is to use this data to ask your preferred AI; I assure you it will give you an easy and simple script to understand.

The amount of code you will need is well below 5kb, including extensive comments.

IMPORTANT:
The difference with this idea is that it’s not actually a backup program, it’s a backup deletion program.

pd
Furthermore, my backup cleaning script is tailored to my needs and I handle external backups, so I add an extra layer that is unnecessary for most people.

That’s essentially what my script does.

It looks large because I included a more elaborate email template.

Also, instead of using v-backup-users, I copied its contents directly into my script. I could call it externally, and the script would be much smaller.

I haven’t read your script, nor have I criticized it.

I got the impression that your script replaces Hestia’s scheduled backup with your own.

Is that correct?
And that’s precisely the point I don’t touch.

When I create a hosting plan, I add 20 backups and let HestiaCP perform its backups at the scheduled time each day.

And my script simply performs cleanup… Depending on the configuration variables, it will allow X daily backups, X weekly backups, X monthly backups…

And I think that’s the only difference.

  • Your script is a backup script.

  • My script is a simple cleanup script.

These are two perfect ideas.

Yup, I know :grin:

Just saying that it is using v-backup-user as base.

The code just looks big. :grimacing:

Major Update (v2): Native Hooks Architecture + Standalone GFS Retention (v-prune-backups)

Quick update for anyone finding this thread — the repository URL has moved to webxtekstudio and this module was completely redesigned into backup-core-patches based on the exact feedback discussed above:

:backhand_index_pointing_right: New URL: hestiacp-useful-tools/scripts/backup-core-patches at main · webxtekstudio/hestiacp-useful-tools · GitHub

What changed:

  1. No more monolithic v-backup-users-custom clone: Instead of duplicating HestiaCP’s backup script, it now uses lightweight native hooks (v-backup-user-hook, v-backup-users-notify-hook, v-backup-user-notify-hook) directly on top of HestiaCP’s native v-backup-user / v-backup-users.
  2. Automatic survival across HestiaCP updates: Includes an APT DPkg::Post-Invoke hook (/etc/apt/apt.conf.d/99-hestia-custom-backup-hooks) that automatically re-links the hooks whenever the hestia package updates.
  3. Standalone GFS Retention CLI (v-prune-backups): Configurable via /etc/hestiacp-backup-retention.conf (KEEP_DAILY_FOR_DAYS, KEEP_WEEKLY_FOR_WEEKS, KEEP_MONTHLY_COUNT) with --dry-run and --apply modes. It prunes both organized local folders (/backup/YYYY/MM_MONTH/user/) and Backblaze B2 buckets while keeping HestiaCP’s backup.conf, U_BACKUPS counter, and /backup/*.tar symlinks in sync.
  4. B2 Cloud Retry & Backup Integrity Verifier (v-verify-backups): Adds a 3-attempt retry wrapper around b2 file upload to prevent transient network failures from aborting backups, plus v-verify-backups to audit local/B2 archive existence, size, and age per user.
  5. Dark-mode HTML email reports with backup.log attached (zero inline log bloat).

Quick Install:

git clone https://github.com/webxtekstudio/hestiacp-useful-tools.git /root/hestiacp-useful-tools
cd /root/hestiacp-useful-tools
bash scripts/backup-core-patches/install.sh

Full toolkit (11 HestiaCP modules including 6-pillar server hardening, CrowdSec nftables, Exim anti-spoofing/size limits, and security audit):

When using v1 at the moment only removing cronjob to /usr/local/hestia/bin/v-backup-users-custom is enough? Then installing v2?

Hi @heart1010! Yes, migrating from v1 to v2 is very simple and 100% backwards-compatible with the backups you already have on disk:

1. Restore the native HestiaCP cron job & remove the old v1 file

In the HestiaCP Admin Panel (Cron Jobs for admin), change the backup command back from v-backup-users-custom to HestiaCP’s native command:

sudo /usr/local/hestia/bin/v-backup-users

Then remove the old standalone v1 script:

sudo rm -f /usr/local/hestia/bin/v-backup-users-custom

2. Install v2 (backup-core-patches)

git clone https://github.com/webxtekstudio/hestiacp-useful-tools.git /root/hestiacp-useful-tools
cd /root/hestiacp-useful-tools
sudo bash scripts/backup-core-patches/install.sh

3. (Optional) Enable automatic GFS retention

By default, v2 leaves automatic pruning disabled (RETENTION_ENABLED="FALSE") as a safety precaution so nobody deletes backups unexpectedly.

If you were using KEEP_ONLY_ONE_OLD_BACKUP="TRUE" in v1:

  1. Edit /etc/hestiacp-backup-retention.conf:

    sudo nano /etc/hestiacp-backup-retention.conf
    
  2. Set your desired windows and enable active pruning:

    RETENTION_ENABLED="TRUE"
    RETENTION_DRY_RUN="FALSE"
    RETENTION_BACKENDS="local"
    KEEP_DAILY_FOR_DAYS=7
    KEEP_WEEKLY_FOR_WEEKS=4
    KEEP_MONTHLY_FOR_MONTHS=12
    
  3. You can preview what it will keep or delete at any time without touching a single file by running:

    sudo /usr/local/hestia/bin/v-prune-backups --dry-run
    

Note on your existing v1 backups: You do not need to move or rename your existing /backup/DD MMM YYYY/username/ folders. All existing symlinks in /backup/ continue to work in the HestiaCP panel, and v-prune-backups automatically scans all subdirectories under /backup/ (both old v1 folders and new v2 /backup/YYYY/MM_MONTH/username/ folders), cleaning up expired archives, symlinks, empty folders, and backup.conf counters automatically.