A background pattern of shaded interlocking triangles

A simple VMWare ESXi Guest Backup Script

The free license option for VMWare ESXi lacks the backup APIs enabled by a vSphere license. Here is a simple script that I use instead to backup my guest VMs on my VMWare ESXi running on a Raspberry Pi 4

Assumptions

The script assumes:

  • All files that make up a VM (disks, machine description, memory, etc) live within a single directory.
  • The name of the directory that contains a VM's files matches the VM's name in the hypervisor. 
  • All of the VMs to be backed-up reside on a single datastore, although the script can be run multiple times to target different datastores
  • That a retention schedule is required that automatically purges aged backups

Copy the script to your ESXi Host

Use SSH, WinSCP or similar to create a copy of the script on your host. I have a datastore directory for scripts, so I copied it there. 

Make the script executable by running:

chmod +x /path/to/backupScript.sh

or by ticking the X box in WinSCPs file properties context menu. 

Configure the script

Using a test editor like Nano, Vi or WinSCP, edit the first section of the script to match your environment and requirements, where:

VariableDescriptionExample
serversToBackupIs a list of guest VMs to backup delimited by a single space, all enclosed in quotes"GuestVm1 GuestVm2"
retentionDaysAn integer that describes how many days backups a retained for before being purged3
sourceFolderThe path to the datastore containing the guest VM folders to backup, all enclosed in quotes

"/vmfs/volumes/

GuestVmDatastore"

 The path to the destination datastore to which backups will be written, all enclosed in quotes

"/vmfs/volumes/

TargetBackupDatastore/

backupDirectory"

The above example would look like:

serversToBackup="GuestVm1 GuestVm2"
retentionDays=3
sourceFolder="/vmfs/volumes/GuestVmDatastore"
destinationFolder="/vmfs/volumes/TargetBackupDatastore/backupDirectory"

Run the script interactively

At this point, you can invoke the script manually from the command line via SSH by simply running:

/path/to/backupScript.sh

However, it is useful to schedule the script to perform a regular automated backup.

Schedule the script to run automatically

Typically in Unix-like operating systems we could schedule job via the root crontab. Whilst this is true for ESXi, a security feature of the system means the content of the file is not persisted between reboots. We have to use a workaround to rewrite our root crontab on each reboot. To do so, edit:

/etc/rc.local.d/local.sh

and append:

kill $(cat /var/run/crond.pid)
echo '5 0 * * * /vmfs/path/to/backupScript.sh 2>&1' >> /var/spool/cron/crontabs/root
crond
exit 0

This snippet kills the cron service, appends our scheduled job to the crontab, and restarts the cron service. Upon restart, the crontab is reread persisting our scheduled job between reboots.

Restore

To recover from a backup, "Register an existing virtual machine" using ESXi and point the interface to the location of your backup files. 

Script code

#!/bin/sh

serversToBackup="GuestVm1 GuestVm2"
retentionDays=3
sourceFolder="/vmfs/volumes/GuestVmDatastore"
destinationFolder="/vmfs/volumes/TargetBackupDatastore"

DATE=$(date +%F)

for SERVER in $serversToBackup; 
    do
        echo "Processing server $SERVER"
        mkdir ${destinationFolder}/`echo $SERVER`_`echo $DATE`
        
        cp -rf ${sourceFolder}/`echo $SERVER`/`echo $SERVER`.vmx   ${destinationFolder}/`echo $SERVER`_`echo $DATE`/`echo $SERVER`.vmx
        cp -rf ${sourceFolder}/`echo $SERVER`/`echo $SERVER`.nvram ${destinationFolder}/`echo $SERVER`_`echo $DATE`/`echo $SERVER`.nvram
        cp -rf ${sourceFolder}/`echo $SERVER`/`echo $SERVER`.vmsd  ${destinationFolder}/`echo $SERVER`_`echo $DATE`/`echo $SERVER`.vmsd

        #get vmid    
      vmid=$(vim-cmd vmsvc/getallvms | grep $SERVER | awk '{print $1}')    

        #take snap
        vim-cmd vmsvc/snapshot.create $vmid backup 'Snapshot created by Backup Script' 0 0
        
        #get disks
        disks=`ls ${sourceFolder}/${SERVER}/*flat.vmdk`
        echo $disks
        disks=`echo $disks | sed 's/-flat//g'`
        echo $disks
            
        #copy snapped disks
        for disk in ${disks}; 
          do 
          targetDisk=`basename ${disk}`
          vmkfstools -i \
            ${disk} \
            ${destinationFolder}/${SERVER}_${DATE}/${targetDisk} \
            -d thin;
          done

        #delete latest (backup) snap
        latestSnap=`vim-cmd vmsvc/snapshot.get $vmid | grep Id | awk '{print $NF}' | tail -1`
        vim-cmd vmsvc/snapshot.remove $vmid $latestSnap;
        
        #retention of x days, purge older
        find ${destinationFolder}/${SERVER}* -mtime +$retentionDays -exec rm -r {} \;
    done

echo "done"