Wake on lan for VM

I have something similar that I wrote for my own needs and it works good so far, decided to share it here. Although I use it only for VMs not containers as my primary use is from Moonlight client to wake up a couple of VM gaming machines. One could adapt it easelly to handle LXC too :

Bash:
#!/bin/bash

# Attempts to start Proxmox VM that matches MAC address received on WOL message
# This could be dangerous if abused by spamming the interface with many packages
# so I would like to try no more than once per 5 seconds.
# In my case useful with Moonlight client
# Uses tcpdump on default proxmox interface, change the interface if needed.
while true; do
  sleep 5
  wake_mac=$(tcpdump -c 1 -UlnXi vmbr0 ether proto 0x0842 or udp port 9 2>/dev/null |\
  sed -nE 's/^.*20:  (ffff|.... ....) (..)(..) (..)(..) (..)(..).*$/\2:\3:\4:\5:\6:\7/p')
  echo "Captured magic packet for address: \"${wake_mac}\""
  echo -n "Looking for existing VM: "
  matches=($(grep -il ${wake_mac} /etc/pve/qemu-server/*))
  if [[ ${#matches[*]} -eq 0 ]]; then
    echo "${#matches[*]} found"
    continue
  elif [[ ${#matches[*]} -gt 1 ]]; then
    echo "${#matches[*]} found, using first found"
  else
    echo "${#matches[*]} found"
  fi
  vm_file=$(basename ${matches[0]})
  vm_id=${vm_file%.*}
  details=$(qm status ${vm_id} -verbose | egrep "^name|^status")
  name=$(echo ${details} | awk '{print $2}')
  status=$(echo ${details} | awk '{print $4}')
  if [[ "${status}" != "stopped" ]]; then
    echo "SKIPPED VM ${vm_id} : ${name} is ${status}"
  else
    echo "STARTING VM ${vm_id} : ${name} is ${status}"
    qm start ${vm_id}
  fi
done
 
Last edited:
I have something similar that I wrote for my own needs and it works good so far, decided to share it here. Although I use it only for VMs not containers as my primary use is from Moonlight client to wake up a couple of VM gaming machines. One could adapt it easelly to handle LXC too :

Bash:
#!/bin/bash

# Attempts to start Proxmox VM that matches MAC address received on WOL message
# This could be dangerous if abused by spamming the interface with many packages
# so I would like to try no more than once per 5 seconds.
# In my case useful with Moonlight client
# Uses tcpdump on default proxmox interface, change the interface if needed.
while true; do
  sleep 5
  wake_mac=$(tcpdump -c 1 -UlnXi vmbr0 ether proto 0x0842 or udp port 9 2>/dev/null |\
  sed -nE 's/^.*20:  (ffff|.... ....) (..)(..) (..)(..) (..)(..).*$/\2:\3:\4:\5:\6:\7/p')
  echo "Captured magic packet for address: \"${wake_mac}\""
  echo -n "Looking for existing VM: "
  matches=($(grep -il ${wake_mac} /etc/pve/qemu-server/*))
  if [[ ${#matches[*]} -eq 0 ]]; then
    echo "${#matches[*]} found"
    continue
  elif [[ ${#matches[*]} -gt 1 ]]; then
    echo "${#matches[*]} found, using first found"
  else
    echo "${#matches[*]} found"
  fi
  vm_file=$(basename ${matches[0]})
  vm_id=${vm_file%.*}
  details=$(qm status ${vm_id} -verbose | egrep "^name|^status")
  name=$(echo ${details} | awk '{print $2}')
  status=$(echo ${details} | awk '{print $4}')
  if [[ "${status}" != "stopped" ]]; then
    echo "SKIPPED VM ${vm_id} : ${name} is ${status}"
  else
    echo "STARTING VM ${vm_id} : ${name} is ${status}"
    qm start ${vm_id}
  fi
done
Excellent. Your script seems to work reliably. I'll keep watching it. Thank you very much for your efforts.
 
I have something similar that I wrote for my own needs and it works good so far, decided to share it here. Although I use it only for VMs not containers as my primary use is from Moonlight client to wake up a couple of VM gaming machines. One could adapt it easelly to handle LXC too :

Bash:
#!/bin/bash

# Attempts to start Proxmox VM that matches MAC address received on WOL message
# This could be dangerous if abused by spamming the interface with many packages
# so I would like to try no more than once per 5 seconds.
# In my case useful with Moonlight client
# Uses tcpdump on default proxmox interface, change the interface if needed.
while true; do
  sleep 5
  wake_mac=$(tcpdump -c 1 -UlnXi vmbr0 ether proto 0x0842 or udp port 9 2>/dev/null |\
  sed -nE 's/^.*20:  (ffff|.... ....) (..)(..) (..)(..) (..)(..).*$/\2:\3:\4:\5:\6:\7/p')
  echo "Captured magic packet for address: \"${wake_mac}\""
  echo -n "Looking for existing VM: "
  matches=($(grep -il ${wake_mac} /etc/pve/qemu-server/*))
  if [[ ${#matches[*]} -eq 0 ]]; then
    echo "${#matches[*]} found"
    continue
  elif [[ ${#matches[*]} -gt 1 ]]; then
    echo "${#matches[*]} found, using first found"
  else
    echo "${#matches[*]} found"
  fi
  vm_file=$(basename ${matches[0]})
  vm_id=${vm_file%.*}
  details=$(qm status ${vm_id} -verbose | egrep "^name|^status")
  name=$(echo ${details} | awk '{print $2}')
  status=$(echo ${details} | awk '{print $4}')
  if [[ "${status}" != "stopped" ]]; then
    echo "SKIPPED VM ${vm_id} : ${name} is ${status}"
  else
    echo "STARTING VM ${vm_id} : ${name} is ${status}"
    qm start ${vm_id}
  fi
done
Started as a manually executed shell script, it works wonderfully. In crontab via: @reboot /root/wol.sh > /dev/null
unfortunately not.
 
I have something similar that I wrote for my own needs and it works good so far, decided to share it here. Although I use it only for VMs not containers as my primary use is from Moonlight client to wake up a couple of VM gaming machines. One could adapt it easelly to handle LXC too :

Bash:
#!/bin/bash

# Attempts to start Proxmox VM that matches MAC address received on WOL message
# This could be dangerous if abused by spamming the interface with many packages
# so I would like to try no more than once per 5 seconds.
# In my case useful with Moonlight client
# Uses tcpdump on default proxmox interface, change the interface if needed.
while true; do
  sleep 5
  wake_mac=$(tcpdump -c 1 -UlnXi vmbr0 ether proto 0x0842 or udp port 9 2>/dev/null |\
  sed -nE 's/^.*20:  (ffff|.... ....) (..)(..) (..)(..) (..)(..).*$/\2:\3:\4:\5:\6:\7/p')
  echo "Captured magic packet for address: \"${wake_mac}\""
  echo -n "Looking for existing VM: "
  matches=($(grep -il ${wake_mac} /etc/pve/qemu-server/*))
  if [[ ${#matches[*]} -eq 0 ]]; then
    echo "${#matches[*]} found"
    continue
  elif [[ ${#matches[*]} -gt 1 ]]; then
    echo "${#matches[*]} found, using first found"
  else
    echo "${#matches[*]} found"
  fi
  vm_file=$(basename ${matches[0]})
  vm_id=${vm_file%.*}
  details=$(qm status ${vm_id} -verbose | egrep "^name|^status")
  name=$(echo ${details} | awk '{print $2}')
  status=$(echo ${details} | awk '{print $4}')
  if [[ "${status}" != "stopped" ]]; then
    echo "SKIPPED VM ${vm_id} : ${name} is ${status}"
  else
    echo "STARTING VM ${vm_id} : ${name} is ${status}"
    qm start ${vm_id}
  fi
done
This was exactly my use case and the solution I was looking for.

I’m not sure what the “right” way to implement it is, but I created a systems service that runs on startup and it seems to work perfectly. Thank you!
 
This was exactly my use case and the solution I was looking for.

I’m not sure what the “right” way to implement it is, but I created a systems service that runs on startup and it seems to work perfectly. Thank you!
Did you compare system service vs. crontab bash?
 
Did you compare system service vs. crontab bash?
I did not, I figured systemd was the way to go, so that's what I went with. Also not sure if it needs to run as root, I based it off of other solutions I found trying to achieve something similar.
Code:
[Unit]
Description=Wake-on-LAN for Proxmox Virtual Environments
After=network.target

[Service]
Type=simple
Restart=always
User=root
ExecStart=/usr/sbin/pve_vm_wol.sh

[Install]
WantedBy=multi-user.target
 
I've adapted your .service-file to my needs and will observe the systemd-solution. I will response message.
 
I have something similar that I wrote for my own needs and it works good so far, decided to share it here. Although I use it only for VMs not containers as my primary use is from Moonlight client to wake up a couple of VM gaming machines. One could adapt it easelly to handle LXC too :

Bash:
#!/bin/bash

# Attempts to start Proxmox VM that matches MAC address received on WOL message
# This could be dangerous if abused by spamming the interface with many packages
# so I would like to try no more than once per 5 seconds.
# In my case useful with Moonlight client
# Uses tcpdump on default proxmox interface, change the interface if needed.
while true; do
  sleep 5
  wake_mac=$(tcpdump -c 1 -UlnXi vmbr0 ether proto 0x0842 or udp port 9 2>/dev/null |\
  sed -nE 's/^.*20:  (ffff|.... ....) (..)(..) (..)(..) (..)(..).*$/\2:\3:\4:\5:\6:\7/p')
  echo "Captured magic packet for address: \"${wake_mac}\""
  echo -n "Looking for existing VM: "
  matches=($(grep -il ${wake_mac} /etc/pve/qemu-server/*))
  if [[ ${#matches[*]} -eq 0 ]]; then
    echo "${#matches[*]} found"
    continue
  elif [[ ${#matches[*]} -gt 1 ]]; then
    echo "${#matches[*]} found, using first found"
  else
    echo "${#matches[*]} found"
  fi
  vm_file=$(basename ${matches[0]})
  vm_id=${vm_file%.*}
  details=$(qm status ${vm_id} -verbose | egrep "^name|^status")
  name=$(echo ${details} | awk '{print $2}')
  status=$(echo ${details} | awk '{print $4}')
  if [[ "${status}" != "stopped" ]]; then
    echo "SKIPPED VM ${vm_id} : ${name} is ${status}"
  else
    echo "STARTING VM ${vm_id} : ${name} is ${status}"
    qm start ${vm_id}
  fi
done
Hey man, THIS version (that uses tcpdump) is the only working one for me. The other one that relies on "nc" does not exit the loop that reads from it. Yours actually does!!! :)

As you suggested, it was easily modified to handle containers. Here is an updated version that will work to boot containers as well as VMs.

Thanks again!!


Bash:
#!/bin/bash

# Attempts to start Proxmox VM or LXC that matches MAC address received on WOL message
# This could be dangerous if abused by spamming the interface with many packages
# so I would like to try no more than once per 5 seconds.
# In my case useful with Moonlight client
# Uses tcpdump on default proxmox interface, change the interface if needed.
while true; do
  sleep 5
  wake_mac=$(tcpdump -c 1 -UlnXi vmbr1 ether proto 0x0842 or udp port 9 2>/dev/null |\
  sed -nE 's/^.*20:  (ffff|.... ....) (..)(..) (..)(..) (..)(..).*$/\2:\3:\4:\5:\6:\7/p')
  echo "Captured magic packet for address: \"${wake_mac}\""
  echo -n "Looking for existing VM: "
  matches=($(grep -il ${wake_mac} /etc/pve/qemu-server/*))
  if [[ ${#matches[*]} -eq 0 ]]; then
    echo "${#matches[*]} found"
  echo -n "Looking for existing LXC: "
  matches=($(grep -il ${wake_mac} /etc/pve/lxc/*))
  if [[ ${#matches[*]} -eq 0 ]]; then
    echo "${#matches[*]} found"
    continue
  elif [[ ${#matches[*]} -gt 1 ]]; then
    echo "${#matches[*]} found, using first found"
  else
    echo "${#matches[*]} found"
  fi
  vm_file=$(basename ${matches[0]})
  vm_id=${vm_file%.*}
  details=$(pct status ${vm_id} -verbose | egrep "^name|^status")
  name=$(echo ${details} | awk '{print $2}')
  status=$(echo ${details} | awk '{print $4}')
  if [[ "${status}" != "stopped" ]]; then
    echo "SKIPPED CONTAINER ${vm_id} : ${name} is ${status}"
  else
    echo "STARTING CONTAINER ${vm_id} : ${name} is ${status}"
    pct start ${vm_id}
  fi
    continue
  elif [[ ${#matches[*]} -gt 1 ]]; then
    echo "${#matches[*]} found, using first found"
  else
    echo "${#matches[*]} found"
  fi
  vm_file=$(basename ${matches[0]})
  vm_id=${vm_file%.*}
  details=$(qm status ${vm_id} -verbose | egrep "^name|^status")
  name=$(echo ${details} | awk '{print $2}')
  status=$(echo ${details} | awk '{print $4}')
  if [[ "${status}" != "stopped" ]]; then
    echo "SKIPPED VM ${vm_id} : ${name} is ${status}"
  else
    echo "STARTING VM ${vm_id} : ${name} is ${status}"
    qm start ${vm_id}
  fi
done
 
  • Like
Reactions: EpicLPer
have added a bit of logging for systemd daemon for better transparency as following(just a sing line example):
Code:
echo -n "Looking for existing VM: " | systemd-cat -t wol_vm -p info
-t is the service name and -p could be also "warning" or "emerg" for example. each "echo" command could be extended with the same
 
thanks for the script and the improvements of the script guys.

I keep getting this error: stdbuf: failed to run command ‘xxd’: No such file or directory

Idk if it's because I'm using PVE 8.0.3, but I would like to use this script for my config.

I had the same problem. the reason is xxd command is missing in the PVE 8 I don't know why. Normally if you will install vim-common with "apt-get install vim-common" command you will see that it is already installed.

but luckily xxd is available independently to install. "apt-get install xxd" will save the day.
 

About

The Proxmox community has been around for many years and offers help and support for Proxmox VE, Proxmox Backup Server, and Proxmox Mail Gateway.
We think our community is one of the best thanks to people like you!

Get your subscription!

The Proxmox team works very hard to make sure you are running the best software and getting stable updates and security enhancements, as well as quick enterprise support. Tens of thousands of happy customers have a Proxmox subscription. Get yours easily in our online shop.

Buy now!