Here’s a script that will loop through the virtual machines in VirtualBox and exports them.

Why not use snapshots?
I’ve seen scripts that will take a snapshot and then copy the ‘raw’ vm over to a backup disk. The advantages of such an approach are:

  • speed: snapshotting a vm is a matter of seconds;
  • continuity: you can snapshot a running vm.

  • But there are drawbacks too:

  • it is more difficult to reassemble a non-exported virtual machine;
  • VirtualBox’s snapshotting system has a nasty habit of refusing to restore or delete individual shapshots – VBox’s snapshotting has not fully matured yet.

  • Why export based backups?
    The killer features for export based backups are obvious:

  • easy of use: you can restore the complete vm including all its settings by just importing the ovm file;
  • portability: any computer running VirtualBox will allow you to import and run the exported machine (you can’t run 64bit machines on 32bit hardware though);
  • stability: the virtual machine’s state will always be consistent since the machine must be taken offline to export it.

  • I’m running two vm’s on my server at home. All their users are local to my time zone and most of them sleep at night so I can afford a bit of downtime. Obviously if you run a bigger operation this may not be convenient. But then again you would probably be running Hyper-V or VMWare.

    vbox-exports

    For each virtual machine this script will:

    1. save the state;
    2. export it to a predefined directory;
    3. restore the previous state;
    4. mail the results to a predefined address.

    I suggest you combine the script with rsnapshot and rsnaptar for historical and off-site backups but that’s up to you.

    Suggestions for improvements are welcome. If you take the script and find it useful please use the PayPal tip jar on the right side of the page or click some ads here :)

    #!/bin/bash
    
    # This scripts loops through all the user's VirtualBox vm's, pauses them,
    # exports them and then restores the original state.
    #
    # VirtualBox's snapshot system is not stable enough for unmonitored use yet.
    #
    # Vorkbaard, 2012-02-01
    
    # =============== Set your variables here ===============
    
      EXPORTDIR=/var/vmbackup
      MYMAIL=your@email.address
      VBOXMANAGE="/usr/bin/VBoxManage -q"
    
    # =======================================================
    
    # Generate a list of all vm's; use sed to remove the double quotes.
    
    # Note: better not use quotes or spaces in your vm name. If you do,
    # consider using the vms' ids instead of friendly names:
    # for VMNAME in $(vboxmanage list vms | cud -t " " -f 2)
    # Then you'd get the ids in your mail so you'd have to use vboxmanage 
    # showvminfo $id or something to retrieve the vm's name. I never use
    # weird characters in my vm names anyway.
    
    for VMNAME in $(vboxmanage list vms | cut -d " " -f 1 | sed -e 's/^"//'  -e 's/"$//')
    do
    
      ERR="nothing"
      SECONDS=0
    
      # Delete old export.log file if it exists
        if [ -e export.log ]; then rm export.log; fi
    
      # Get the vm state
        VMSTATE=$(vboxmanage showvminfo $VMNAME --machinereadable | grep "VMState=" | cut -f 2 -d "=")
        echo "$VMNAME's state is: $VMSTATE."
    
      # If the VM's state is running or paused, save its state
        if [[ $VMSTATE == \"running\" || $VMSTATE == \"paused\" ]]; then
          echo "Saving state..."
          vboxmanage controlvm $VMNAME savestate
          if [ $? -ne 0 ]; then ERR="saving the state"; fi
        fi
    
      # Export the vm as appliance
        if [ "$ERR" == "nothing" ]; then
          echo "Exporting the VM..."
          vboxmanage export $VMNAME --output $EXPORTDIR/$VMNAME-new.ova &> export.log
          if [ $? -ne 0 ]; then
            ERR="exporting"
          else
            # Remove old backup and rename new one
           if [ -e $EXPORTDIR/$VMNAME.ova ]; then rm $EXPORTDIR/$VMNAME.ova; fi
           mv $EXPORTDIR/$VMNAME-new.ova $EXPORTDIR/$VMNAME.ova
           # Get file size
           FILESIZE=$(du -h $EXPORTDIR/$VMNAME.ova | cut -f 1)
          fi
        else
          echo "Not exporting because the VM's state couldn't be saved." &> export.log
        fi
    
      # Resume the VM to its previous state if that state was paused or running
        if [[ $VMSTATE == \"running\" || $VMSTATE == \"paused\" ]]; then
            echo "Resuming previous state..."
            vboxmanage startvm $VMNAME --type headless
            if [ $? -ne 0 ]; then ERR="resuming"; fi
            if [ $VMSTATE == \"paused\" ]; then
              vboxmanage controlvm $VMNAME pause
              if [ $? -ne 0 ]; then ERR="pausing"; fi
            fi
          fi
    
      # Calculate duration
        duration=$SECONDS
        duration="Operation took $(($duration / 60)) minutes, $(($duration % 60)) seconds."
    
      # Notify the admin
        if [ "$ERR" == "nothing" ]; then
          MAILBODY="Virtual Machine $VMNAME was exported succesfully!"
          MAILBODY="$MAILBODY"$'\n'"$duration"
          MAILBODY="$MAILBODY"$'\n'"Export filesize: $FILESIZE"
          MAILSUBJECT="VM $VMNAME succesfully backed up"
        else
          MAILBODY="There was an error $ERR VM $VMNAME."
          if [ "$ERR" == "exporting" ]; then
            MAILBODY=$(echo $MAILBODY && cat export.log)
          fi
          MAILSUBJECT="Error exporting VM $VMNAME"
        fi
    
      # Send the mail
        echo "$MAILBODY" | mail -s "$MAILSUBJECT" $MYMAIL
    
      # Clean up
        if [ -e export.log ]; then rm export.log; fi
    
    done
    

    My HP ProLiant ML370 G4 exports a midsize Windows Server in about an hour. It exports a PfSense router installation in under three minutes. Your mileage may vary so best test the export time needed before taking the script in production.

    The web interface you see here is phpVirtualBox, a very handy piece of software to have on your VirtualBox server. Learn how to set it up.