When using VirtualBox on a server in production, you will want to backup it. For that to work really reliable, the VM has to be in a consistent state, or you would risk your backup to be nonfunctional, lose data, grow a conscience...

The simplest way to put a VM into a consistent state is shutting it down. So the plan will be:

  1. Shut down
  2. Backup
  3. Boot up

Shutting down and Booting up a VM on a contemporary server should not take too long, however backupping does. Fortunately we have zfs, which allows us to take a quick snapshot of the powered down VM, which takes less than a second, boot up the VM and do the backup later on.

So that's what I intend to do:

A VirtualBox VM is shut down through the command:

VBoxManage controlvm SchneckVM acpipowerbutton

This requires that the VM listens to acpi events. Debian based Linux Systems do that using the acpid package.

Then wait for the Machine to be powered down. You can check with

VBoxManage list runningvms

which machines are still running.

Do the zfs magic

zfs snapshot rpool/schneckvm@Backup

Then power the VM up:

VBoxManage startvm SchneckVM -type headless

Some notes:

If you intend to use a separate vm user like I did and execute the commands via sudo, then add the -H parameter, otherwiese VirtualBox won't find the VMs. (At least if there are no other VMs running)

Starting a headless VM with VirtualBox (PUEL) will also set up the VRDP server regardless of the VM preferences. So if don't need that, switch it off using:

VBoxManage controlvm SchneckVM vrde off

Of course, SchneckVM is our example VM above.

And now the good Stuff:

#!/bin/bash
LOGFILE="/var/log/vm-snapshot.log"
maxRetries="30"
sleeptime="7"
VMUSER=vbox
VMDATASET=rpool/vm
VBoxManage=/opt/VirtualBox/VBoxManage
RUNNINGVMS=`sudo -H -u $VMUSER $VBoxManage list runningvms`
SNAPSHOT=`date +%Y%m%d`


#Logging method. Writes the given string out into logfile.
#@param the string to log
log(){
echo $* 2>&1 >>$LOGFILE
}
# Shut down running vms

log "Starting Snapshot Process at `date`"
log "Running VMS:"
log "$RUNNINGVMS"

RUNNINGVMS=`echo $RUNNINGVMS | cut -d \ -f 2`

for i in $RUNNINGVMS; do
log "Stopping $i"
log "`sudo -H -u $VMUSER $VBoxManage controlvm $i acpipowerbutton`"
done

# now wait for the machines to shut down

log "waiting for shutdown max retries: $maxRetries"

I=1
while [ $I -le $maxRetries ] ;
do
log $I
I=`expr $I + 1`
sleep $sleeptime
MACHINESSTILLRUNNING=`sudo -H -u $VMUSER $VBoxManage list runningvms`
if [ "x$MACHINESSTILLRUNNING" = "x" ] ;
then break
fi
log "VMs are still running"
log "$MACHINESSTILLRUNNING"
done

if [ "x$MACHINESSTILLRUNNING" != "x" ] ; then
log "Timeout, there are still machines running, powering off forcibly"
log "$MACHINESSTILLRUNNING"
MACHINESSTILLRUNNING=`echo $MACHINESSTILLRUNNING | cut -d \ -f 2`
for i in $MACHINESSTILLRUNNING; do
log "`sudo -H -u $VMUSER $VBoxManage controlvm $1 poweroff`"
done
fi
# do zfs snapshot magic
log "Making the zfs snapshot $VMDATASET@Backup$SNAPSHOT"
log "`sudo -H zfs snapshot $VMDATASET@Backup$SNAPSHOT`"

# start the VMs again
log "starting the VMs again"
for i in $RUNNINGVMS; do
log "Starting $i"
log "`sudo -H -u $VMUSER $VBoxManage startvm $i -type headless`"
log "`sudo -H -u $VMUSER $VBoxManage controlvm $i vrde off`"
done

If you are looking for a similar script on Linux, go to Virtualbox Backup Skript on which this script is based.

Stay tuned for part II, where the snapshot is then synced to a remote backup server.