Backup script for VirtualBox vm’s in Debian

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

Note: a new version of this script is available here.

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.

    16 Comments

    1. Jefferson Elias

      Hi, first of all, thank you for this script that I will test.
      By the way, one thing that could be great is to be able to filter on machines which are meant for backup and those which are not (example: test/dev machines)

    2. Jefferson Elias

      First correction: replaced the cut delimiter from space to “{” because if you use spaces in machine name, it can be problematic.

      Complete command:

      vboxmanage list vms | cut -d “{” -f 1 | sed -e ‘s/^”//’ -e ‘s/” $//’

    3. Jefferson Elias

      Second change:
      before the mail loop, I added this:

      OLD_IFS=”$IFS”
      IFS=$’\n’

      And after the loop, that:
      IFS=”$OLD_IFS”
      unset OLD_IFS

      This allows to get the accurate list of VMs if there are spaces in the names.

      Regards

      • Kapitein Vorkbaard

        I think you need to do things with systemd units. I’m not sure how Ubuntu does that but I guess there must be a manual somewhere.

      • Kapitein Vorkbaard

        Hi Fabricio, I’m sorry but this script is just Bash. I hear Bash is an app in Windows nowadays so perhaps that’s a approach you can investigate. Of just use PowerShell. It isn’t terribly complicated to create loops in PowerShell and the vboxmanage command is available and the same in any platform you can install VirtualBox on.

    4. Joan

      Hi, are u having any problems when recovering to machine status?
      I’m using it to backup some Virtualbox machines, and last weeks the machines don’t return to their previous state, seems can’t start or recover and finally stay as aborted.
      We are using phpVirtualBox 5.2-0 -/- VirtualBox 5.2.x compatible and Virtual Box 5.2.10 (122088).
      I hope you have some news.

      Thanks

    5. Bruno Forcier

      Hello,

      Any easy way to put in the script that I want the actual ‘easy’ name of the VM, not the UUID in the name string of the .OVA?

      For example the ova backup file could be called

      ns1_99904603-29e5-4907-bf63-bd74f1237c91.ova

      So, ‘easy name’ of the vm followed by _ followed by UUID followed by .ova?

      Also, the actual file is called {f4a5e5bd-0c3d-4b62-ba33-fc0e06e1cc2e}.ova

      Are the {} important?

      Thanks

      • Bruno Forcier

        I knew I had forgotten a question I wanted to ask:

        One other newbie question in regards to VirtualBox on Linux (VB 6.1.x running on Ubuntu 16.04.6 LTS in GUI mode since I never managed to get console working using phpvirtualbox for headless purposes)

        I keep calling those strings UUIDs, is this what they are?

        Thanks again!

    Leave a Reply

    Your email address will not be published. Required fields are marked *

    This site uses Akismet to reduce spam. Learn how your comment data is processed.