Proxmox - Execute Script on Host

We sometimes need to execute a script on our host when starting or stopping a VM.
I was using another solution not elegant that broke after a proxmox update.
And after researching the proxmox documentation a supported solution is available.
We first need to edit our /etc/pve/storage.cfg adding the snippets directory to the host as below :

dir: local
        path /var/lib/vz
        content backup,vztmpl,iso,snippets

# Entry added manually for the ZFS local storage after the migration
zfspool: local-zfs
        pool rpool/data
        sparse
        content images,rootdir

lvmthin: HV2-FW2
        thinpool HV2-FW2
        vgname HV2-FW2
        content images,rootdir
        nodes hv2

After refreshing our proxmox GUI the folder snippets will be created according to our /etc/pve/storage.cfg according to the output below :

root@hv2:/var/lib/vz/snippets# ll /var/lib/vz/
total 26
drwxr-xr-x 2 root root 4 Aug 26  2020 dump
drwxr-xr-x 2 root root 2 Dec  3  2019 images
drwxr-xr-x 2 root root 3 Jul  1 13:35 snippets
drwxr-xr-x 5 root root 5 Jan  9  2020 template

Copy the example script to the snippets folder with the command below :

root@hv2:~# cp /usr/share/pve-docs/examples/guest-example-hookscript.pl /var/lib/vz/snippets/network.pl

We could write a custom perl script, but unfortunately, I am not familiar with perl and the example script is detailed enough to be used.

The documentation is not clear if a bash script is supported. I will eventually test and update the article if it works.

The script is executed in 4 stages of the VM life ( pre-start, post-start, pre-stop, post-stop ) and we are interested in the post-start stage since the script to be executed in the host will configure the VM network.

We will copy the example script to the recently created folder /var/lib/vz/snippets/network.pl and change the post-start block to execute the desired bash script as seen below :

#!/usr/bin/perl

# Exmple hook script for PVE guests (hookscript config option)
# You can set this via pct/qm with
# pct set <vmid> -hookscript <volume-id>
# qm set <vmid> -hookscript <volume-id>
# where <volume-id> has to be an executable file in the snippets folder
# of any storage with directories e.g.:
# qm set 100 -hookscript local:snippets/hookscript.pl

use strict;
use warnings;

print "GUEST HOOK: " . join(' ', @ARGV). "\n";

# First argument is the vmid

my $vmid = shift;

# Second argument is the phase

my $phase = shift;

if ($phase eq 'pre-start') {

    # First phase 'pre-start' will be executed before the guest
    # ist started. Exiting with a code != 0 will abort the start


    print "$vmid is starting.\n";

    # print "preparations failed, aborting."
    # exit(1);

} elsif ($phase eq 'post-start') {

    # Second phase 'post-start' will be executed after the guest
    # successfully started.
    print "$vmid started, executing network script.\n";
    # I believe that the output should be captured and tested
    # to return the correct exit code.
    system("/bin/bash /root/network/config/hv2_net_vps.sh $vmid")


} elsif ($phase eq 'pre-stop') {

    # Third phase 'pre-stop' will be executed before stopping the guest
    # via the API. Will not be executed if the guest is stopped from
    # within e.g., with a 'poweroff'

    print "$vmid will be stopped.\n";

} elsif ($phase eq 'post-stop') {

    # Last phase 'post-stop' will be executed after the guest stopped.
    # This should even be executed in case the guest crashes or stopped
    # unexpectedly.

    print "$vmid stopped. Doing cleanup.\n";

} else {
    die "got unknown phase '$phase'\n";
}

exit(0);
/var/lib/vz/snippets/network.pl

I have added the line :

system("/bin/bash /root/network/config/hv2_net_vps.sh $vmid")

I am using the function system() to invoke a bash script passing the VMID as an argument. You can adapt the example to your needs.

It worth mention that I could not see the pre-stop or post-stop be executed when the VM was shut down from within its OS. Although stated on the example that post-stop should be executed when the VM is stopped from the OS or crashed.

The last step is to execute the command below to activate the hookscript :

qm set 101 --hookscript local:snippets/network.pl

101 - is your VM_ID.

We now can see the script being executed when the VM starts.

External Resources

Proxmox VE Administration Guide