In my particular case, with a new server installation of Ubuntu 18.04 Bionic and VirtualBox-5.2.20, it appears the old method of auto-starting guests is no longer available. Not that it was that wonderful - but it functioned. There doesn't appear to be any clean way to go about this - how can systemd and VirtualBox work together for intelligent booting, control, and shutdown?
2 Answers
Similar to your solution, but a bit simpler:
- Run
systemctl edit [email protected] --full --force
and paste the following content in, updating User and Group to your username.
[Unit]
Description=Virtual Box Guest %I
After=network.target vboxdrv.service
Before=runlevel2.target shutdown.target
[Service]
User=USERNAME
Group=GROUPNAME
Type=forking
Restart=no
TimeoutSec=5min
IgnoreSIGPIPE=no
KillMode=process
GuessMainPID=no
RemainAfterExit=yes
ExecStart=/usr/bin/VBoxManage startvm %i --type headless
ExecStop=/usr/bin/VBoxManage controlvm %i acpipowerbutton
[Install]
WantedBy=multi-user.target
Reload systemd:
systemctl daemon-reload
Get a list of your VM's
VBoxManage list vms
:
$ VBoxManage list vms
"Ubuntu" {1ba32309-d4c4-420a-a9c8-a38177f00bc4}
"Windows" {573df054-0e33-4389-896a-1234f10e25ad}
- Use the name returned in step 3 to manage the VM via systemd. For example, to manage the "Ubuntu" VM you would run:
sudo systemctl start vbox@Ubuntu # Start the VM
sudo systemctl enable vbox@Ubuntu # Start the VM on boot
Well...I think I came up with an answer.
This is my solution. It's certainly not perfect - and part of the reason I'm sharing is to invite improvement. However - this provides for autostart, monitoring, and shutdown via systemd. And I think this is at least going in the right direction for these two products.
One caveat - the below presumes a little familiarity with both systemd & VirtualBox. It also requires ACPI shutdown functions enabled on the guests. That may be built-in to Windows, and as simple as installing acpid on Linux, but I'm not assuming. Also some guests (Windows version xxxx) may need some "tweaking" to ensure ACPI shutdowns happen immediately - I found a wonderful resource at https://ethertubes.com/unattended-acpi-shutdown-of-windows-server/
First - need to create a systemd unit of course. I'm leveraging the availability of templates.
executing systemctl edit --full [email protected]
provides an editor into which we place:
[Unit]
Description=VirtualBox %I Virtual Server
After=network.target vboxdrv.service
[Service]
Type=forking
Restart=no
TimeoutSec=5min
KillMode=process
RuntimeDirectory=vbox
RuntimeDirectoryPreserve=yes
PIDFile=/run/vbox/%I.pid
Environment='RUNDIR=/run'
Environment='PIDDIR=/vbox'
Environment='VM=%I'
ExecStart=/etc/init.d/vbox-systemd start
ExecStop=/etc/init.d/vbox-systemd stop
[Install]
WantedBy=multi-user.target
The above provides a basis:
- Allows for up to 5 minutes for startup/shutdown per guest
- The pid files will be stored as /run/vbox/<guest>.pid
- And the guests will be started as part of the normal boot process
This can be adjusted to taste - but apart from server-wide defaults leave this alone. Individual guests will be tailored later. Now - need to provide the helper script. I just spent an extended period of time fighting with the intricacies of BASH, a lack of sleep, and a severe lack of active experience with BASH. So the following works, there's a mix of styles, and I really wish I knew why my attempt at simple BASH functions failed so miserably. But I'm ready for bed so while it's not my best work it...it does work!:
#! /bin/bash
# /etc/init.d/vbox-systemd: Helper script to startup & shutdown VirtualBox
# headless machines via systemd
#
# written by Daniel L. Miller <[email protected]>
# This should not be called directly (though possible with the
# proper environment variables set). This is used by the
# [email protected] template to start & stop virtual machines -
# with supervision.
# Environment variables to be defined for us by systemd unit
# RUNDIR=/run
# PIDDIR=/vbox
# VM=<vmname>
# This was setup to use environment variables - maybe support cmd line as well.
if [ ! -z "$2" ]; then
VM=$2
fi
# So...I suppose might as well set sane defaults
if [ -z "$RUNDIR" ]; then
RUNDIR='/run'
fi
if [ -z "$PIDDIR" ]; then
PIDDIR='/vbox'
fi
#
# Overprotective but trying to be good...
# These utilities should be fairly standard...
#
VB=/usr/bin/VBoxManage
GREP=/bin/grep
CUT=/usr/bin/cut
TR=/usr/bin/tr
SLEEP=/bin/sleep
WAITEXIT=300
# Make sure the utilities are available
test -x $VB || exit 3
test -x $GREP || exit 3
test -x $CUT || exit 3
test -x $TR || exit 3
# Verify the pid folder tree is defined and usable
test -d "${RUNDIR:?run directory top-level must be set}" || exit 3
test -d "$RUNDIR${PIDDIR:?pid directory must be set}" || mkdir -p "$RUNDIR$PIDDIR"
# This test is a little different - this validates the name but we don't
# care if the file exists or not. At least the moment.
test -f "$RUNDIR$PIDDIR/${VM:?Virtual Machine name must be set}.pid"
PIDFILE=$RUNDIR$PIDDIR/$VM.pid
vmactive=$($VB list runningvms | grep $VM | cut -d ' ' -f 1 | tr -d '"')
case "${1:-''}" in
'start')
# Start the machine
$VB startvm $VM --type headless
# Give it at least a change to get started...
$SLEEP 2
# Now perform first trick and save pid
vmactive=`$VB list runningvms | grep $VM | cut -d ' ' -f 1 | tr -d '"'`
if [ "x$vmactive" == "x$VM" ]; then
vmpid=$($VB showvminfo $VM --log 0 | $GREP -m 1 'Process ID' | $CUT -d ':' -f4 | $TR -d ' ')
echo $vmpid > $PIDFILE
else
exit 1;
fi
;;
'stop')
waited=0
while [ "$waited" -lt $WAITEXIT ]; do
# Test first so VB doesn't object to shutting off a non-running VM
vmactive=`$VB list runningvms | grep $VM | cut -d ' ' -f 1 | tr -d '"'`
if [ "x$vmactive" != "x$VM" ]; then
echo "Proper ACPI Shutdown of $VM - or it wasn't running!"
break
fi
# Try to turn it off - repeatedly
$VB controlvm $VM acpipowerbutton
# Give it a chance to take.
$SLEEP 5
waited=$((waited+5))
done
# Time to clean up - force terminate if necessary and delete the pid file
[ "$waited" -ge $WAITEXIT ] && [ -f $PIDFILE ] && kill -s 9 $PIDFILE
[ -f $PIDFILE ] && rm $PIDFILE
;;
'status')
vmactive=`$VB list runningvms | grep $VM | cut -d ' ' -f 1 | tr -d '"'`
if [ "x$vmactive" == "x$VM" ]; then
vmpid=$($VB showvminfo $VM --log 0 | $GREP -m 1 'Process ID' | $CUT -d ':' -f4 | $TR -d ' ')
echo "$VM is running as PID $vmpid"
else
echo "$VM is not running"
fi
;;
*)
echo "Usage: vbox-systemd [start|stop|status]" >&2
exit 3;
;;
esac
So...now all that's necessary to start a virtual machine is: systemctl start vbox@<your-guest-name>
. More exciting - systemctl status vbox@<your-guest-name>
will provide systemd status of the VM! And you can even do a systemctl stop vbox@<your-guest-name>
to turn it off.
To activate auto-start - simply run systemctl enable vbox@<your-guest-name>
.
Now - if you need extra control, like specifying the order in which the guests boot, use the tricky systemctl edit vbox@<your-guest-name>
.
Notice this time we're not using the --full
argument - this creates an override folder just for this guest without duplicating the base unit. Maybe this guest needs SQL services from the host:
[Unit]
After=mysql.service
Wants=mysql.service
Now this guest won't be started until the mysql server is up. Or if this guest provides critical services you can add:
[Services]
Restart=yes
Remember - just put in the systemd arguments you need to add or override - the rest come from the template.
I hope this helps someone else - and if others can contribute please do!
-
Why write a Systemd service file and a SysV Init? Just stick with Systemd as in serverfault.com/a/982333/270005 Jan 15, 2021 at 21:32