Backup Script
2026-05-24
The problem and the approach to solving it
We have used a third party for our primary backup system for a long time. We also use JetBackup as a secondary daily backup that pushes to cloud storage, but the other backup at this other company was more convenient. However, due to some recent changes with that service and the expense annual, it was time to look at something else.
Having been using bash a lot for work lately, it only made sense to roll my own backup solution instead of finding another service to use. The annual savings alone made it very appealing.
I really wasn’t interested in getting entrenched in yet another third party either. I am fairly sick of anything even remotely in the realm of Saas products. They turn what should be boring, stable, and dependable code into a nightmarish bloated mess that’s prone to conflicts and of course is offered in a subscription model. The subscription model makes endlessly changing things necessary, and you wind up with a unstable, bloated, expensive tool you only use some of. No thanks.
We already host websites. We already know how to take backups. We have things we can hook into and trigger. Why not make our own on demand backup solution!
Overview of how it works
The user experience is quite simple. You simply run:
backup CPANELUSERNAMEWe are using JetBackup’s great CLI support to trigger an on demand backup. The script first takes the cPanel username and looks up that account’s JetBackup ID to begin interacting with JetBackup.
Upon running the command, the user is shown a short print out of information ending with a message explaining that the backup has been successfully initiated. We also receive emails when the backup is started and completed.
Once completed, you can access the backup in the JetBackup page in that site’s cPanel account. I chose not to restore from terminal though. I want to go in, see the date/time, and have the option to restore the entire cPanel or parts - all of which is something I didn’t want to script as it would be far more complicated than the already present method for restoring with JetBackup.
I would have never created this on demand backup script if JetBackup had a way to trigger a backup from within cPanel. I like doing this stuff, but when it comes to work - it’s about getting the job done and not showing off or creating something just to create something. I have plenty else to do.
The technicals
I’ve intentionally simplified a few things below, but all of this is based on the actual script.
1. Start with a simple command
I didn’t want to log into another service, click through pages, search for an account, and manually trigger a backup. We already have a backup service on our server and needed a quick, low effort way to trigger it.
I wanted:
backup-cpanelusernameSimple. Fast. Easy enough for anyone to use.
2. Accept the cPanel username as input
The script starts by taking the cPanel username passed into the command.
USER="$1"This allows the script to work against any account on the server without hard coding anything.
3. Validate the input
Before doing anything else, the script checks whether a username was actually provided.
if [ -z "$USER" ]; then
echo "Usage: backup-cpanel cpaneluser"
exit 1
fiVery basic, but important. The if we entered something that isn’t an actual username, it stops right there.
4. Pull the JetBackup account list
JetBackup internally uses its own account IDs. We don’t know those IDs offhand and frankly I don’t care what they are. We have the cPanel usernames readily available and identifiable, so that is what we used.
The script pulls the JetBackup account list as JSON:
TMP="/root/jbaccounts.json"
jetbackup5api -F listAccounts -O json -D "limit=1000" > "$TMP"At that point we have a local JSON file containing all backup accounts.
5. Extract the matching JetBackup account ID
This is probably the most important part of the script.
The script searches the JSON output for the matching cPanel username and extracts the corresponding JetBackup account ID.
ACCOUNT_ID=$(grep -o "\"_id\":\"[^\"]*\",\"uuid\":\"[^\"]*\",\"username\":\"$USER\"" "$TMP" \
| head -n1 \
| sed -E 's/"_id":"([^"]+)".*/\1/')ChatGPT says this is not very pretty JSON parsing and that
jq could have been used, but that if this works - it’s fine
for our internal tool.
6. Stop immediately if the account isn’t found
If the lookup fails, the script exits immediately instead of trying to guess.
if [ -z "$ACCOUNT_ID" ]; then
echo "ERROR: Could not find JetBackup account ID for user: $USER"
exit 1
fi7. Queue the backup through JetBackup
Once the correct account ID is found, the script triggers JetBackup’s Backup on Demand feature.
jetbackup5api -F createBackupOnDemand -D "account_id=$ACCOUNT_ID"This was a very intentional design decision.
I did not want to reinvent: - backup retention - backup storage - restore systems - database dumping - archive management
JetBackup already handles all of that extremely well. It’s nice that our on demand backups are showing in the same place as our daily backups. This plays into the staging script I will write about in another post.
8. Print useful output to the terminal
The script prints out what it’s doing as it runs:
echo "Looking up JetBackup account ID for: $USER"
echo "Found account_id: $ACCOUNT_ID"
echo "Triggering JetBackup Backup on Demand..."Again, simple, but important. If it works - it lets you know. If it fails - it lets you know.
9. Keep the restore process manual
One thing I intentionally did not automate was restores.
Once the backup finishes, I still restore through the JetBackup UI.
That was very deliberate.
The UI already provides: - restore points - timestamps - partial restores - full account restores - database restores - file browsing
Trying to rebuild all of that in Bash would have been unnecessary complexity for very little gain.
Conclusion
This script has been in use for about a week now and is working perfectly. I have not had to adjust it once. It just plain works. It’s also saving us money because we didn’t have to find a new backup solution.