diff --git a/build_iso.sh b/build_iso.sh
index d40cd64..1dcacfe 100755
--- a/build_iso.sh
+++ b/build_iso.sh
@@ -88,10 +88,17 @@ echo "*** Copying isolinux.cfg to syslinux.cfg for grml2usb support ***"
 cp ${TEMPLATES}/boot/isolinux/isolinux.cfg ${TEMPLATES}/boot/isolinux/syslinux.cfg
 
 echo "*** Generating Sipwise ISO ***"
-sudo /usr/sbin/grml2iso -c ./${TEMPLATES} -o "${SIPWISE_ISO}" "${GRML_ISO}"
 
-echo "*** Generating dd-able ISO ***"
-sudo /usr/bin/isohybrid "${SIPWISE_ISO}"
+if [[ ! -d grml2usb.git ]] ; then
+  GRML2USB_VERSION='v0.17.0'
+  if ! git clone -b "${GRML2USB_VERSION}" --single-branch --depth 1 https://github.com/grml/grml2usb grml2usb.git ; then
+    echo "Cloning grml2usb from github failed, falling back to git.grml.org"
+    git clone -b "${GRML2USB_VERSION}" --single-branch --depth 1 git://git.grml.org/grml2usb.git
+  fi
+fi
+GRML2USB_DIR=$(pwd)/grml2usb.git
+
+sudo GRML2USB="${GRML2USB_DIR}/grml2usb" "${GRML2USB_DIR}/grml2iso" -c ./${TEMPLATES} -o "${SIPWISE_ISO}" "${GRML_ISO}"
 
 sudo implantisomd5 "${SIPWISE_ISO}"
 
@@ -103,4 +110,7 @@ mkdir -p artifacts
 echo "*** Moving ${SIPWISE_ISO} ${SIPWISE_ISO}.sha1 ${SIPWISE_ISO}.md5 to artifacts/ ***"
 mv "${SIPWISE_ISO}" "${SIPWISE_ISO}.sha1" "${SIPWISE_ISO}.md5" artifacts/
 
+docker run --rm -i --privileged -v "$(pwd)":/code/ docker.mgm.sipwise.com/deployment-iso-buster:latest \
+  /code/t/iso-tester /code/artifacts/"${SIPWISE_ISO}" /code/artifacts/memtest.jpg /code/t/screenshots/01-memtest.jpg
+
 popd &>/dev/null
diff --git a/grml_build/Dockerfile b/grml_build/Dockerfile
index 62f9fd8..ecdd532 100644
--- a/grml_build/Dockerfile
+++ b/grml_build/Dockerfile
@@ -5,9 +5,10 @@ FROM docker.mgm.sipwise.com/sipwise-buster:latest
 # is updated with the current date. It will force refresh of all
 # of the base images and things like `apt-get update` won't be using
 # old cached versions when the Dockerfile is built.
-ENV REFRESHED_AT 2019-08-01
+ENV REFRESHED_AT 2019-11-08
 
-RUN apt-get update && apt-get install --assume-yes \
+# tools for building and testing
+RUN apt-get update && apt-get install --assume-yes --no-install-recommends \
   bc \
   bzip2 \
   dosfstools \
@@ -15,6 +16,7 @@ RUN apt-get update && apt-get install --assume-yes \
   fai-server \
   git \
   grml2usb \
+  imagemagick \
   isolinux \
   isomd5sum \
   kmod \
@@ -23,38 +25,57 @@ RUN apt-get update && apt-get install --assume-yes \
   moreutils \
   mtools \
   pciutils \
+  qemu-system-x86 \
   rsync \
+  socat \
   squashfs-tools \
   sudo \
   syslinux \
+  wget \
   xorriso
 
-RUN echo "SECURE_BOOT=disable ./grml-live -s buster -a amd64 -c GRMLBASE,SIPWISE,AMD64 -t /code/grml-live/templates/ -o /grml/ -r grml-sipwise -v 0.42" >/root/.bash_history
-RUN echo "export LIVE_CONF=/code/grml-live/etc/grml/grml-live.conf" >>/root/.bash_history
-RUN echo "export SCRIPTS_DIRECTORY=/code/grml-live/scripts"         >>/root/.bash_history
-RUN echo "export GRML_FAI_CONFIG=/code/grml-live/etc/grml/fai"      >>/root/.bash_history
-RUN echo "cp /deployment-iso/grml_build/package_config/SIPWISE /code/grml-live/etc/grml/fai/config/package_config/SIPWISE" >>/root/.bash_history
+RUN echo "SECURE_BOOT=disable ./grml-live -s buster -a amd64 -c GRMLBASE,SIPWISE,AMD64 -t /code/grml-live/templates/ -o /grml/ -r grml-sipwise -v 0.42" >/root/.bash_history && \
+  echo "export LIVE_CONF=/code/grml-live/etc/grml/grml-live.conf" >>/root/.bash_history && \
+  echo "export SCRIPTS_DIRECTORY=/code/grml-live/scripts"         >>/root/.bash_history && \
+  echo "export GRML_FAI_CONFIG=/code/grml-live/etc/grml/fai"      >>/root/.bash_history && \
+  echo "cp /deployment-iso/grml_build/package_config/SIPWISE /code/grml-live/etc/grml/fai/config/package_config/SIPWISE" >>/root/.bash_history
 
+# base build tools
 WORKDIR /code/
-RUN git clone https://github.com/grml/grml-live
-
-WORKDIR /code/grml-live
-
-RUN mkdir -p /code/grml-live/etc/grml/fai/config/files/etc/apt/sources.list.d/sipwise.list/ \
+RUN git clone -b 'v0.34.3' --single-branch --depth 1 https://github.com/grml/grml-live
+RUN mkdir -p /code/grml-live/templates/boot/addons/ \
+  /code/grml-live/etc/grml/fai/config/files/etc/apt/sources.list.d/sipwise.list/ \
   /code/grml-live/etc/grml/fai/config/files/etc/apt/trusted.gpg.d/sipwise-keyring.gpg/ \
   /code/grml-live/etc/grml/fai/config/files/etc/apt/trusted.gpg.d/sipwise-keyring-bootstrap.gpg/ \
   /code/grml-live/etc/grml/fai/config/files/root/puppet.gpg/ \
   /code/grml-live/etc/grml/fai/config/scripts/PUPPETLABS/
 
+# addons
+RUN wget https://debian.sipwise.com/debian/pool/main/m/memtest86+/memtest86+_5.01-3_amd64.deb && \
+  dpkg -x memtest86+_5.01-3_amd64.deb /tmp/memtest86 && \
+  cp /tmp/memtest86/boot/memtest86+.bin /code/grml-live/templates/boot/addons/memtest && \
+  rm -rf /tmp/memtest86
+
+RUN wget -O /code/grml-live/templates/boot/addons/netboot.xyz.efi https://boot.netboot.xyz/ipxe/netboot.xyz.efi && \
+  wget -O /code/grml-live/templates/boot/addons/netboot.xyz.lkrn https://boot.netboot.xyz/ipxe/netboot.xyz.lkrn
+
+RUN wget http://ftp.de.debian.org/debian/pool/main/i/ipxe/ipxe_1.0.0+git-20190125.36a4c85-1_all.deb && \
+  dpkg -x ipxe_1.0.0+git-20190125.36a4c85-1_all.deb /tmp/ipxe && \
+  cp /tmp/ipxe/boot/ipxe.efi /code/grml-live/templates/boot/addons/ipxe.efi && \
+  cp /tmp/ipxe/boot/ipxe.lkrn /code/grml-live/templates/boot/addons/ipxe.lkrn && \
+  rm -rf /tmp/ipxe
+
+WORKDIR /code/grml-live
+
 ### Usage instructions #############################################################################
 ## Build docker image:
 #
-# docker build --tag="grml-sipwise" -f grml_build/Dockerfile
+# docker build --tag="grml-sipwise" -f grml_build/Dockerfile .
 #
-## Build GRML image:
+## Build Grml image (assuming current working directory is deployment-iso.git):
 #
 # mkdir -p grml/
-# docker run --rm -i -t --privileged -v deployment-iso.git:/deployment-iso/ -v $(pwd)/grml:/grml/ grml-sipwise
+# docker run --rm -i -t --privileged -v $(pwd):/deployment-iso/ -v $(pwd)/grml:/grml/ grml-sipwise
 #
 ## inside docker container (also available in shell history):
 #
@@ -67,4 +88,4 @@ RUN mkdir -p /code/grml-live/etc/grml/fai/config/files/etc/apt/sources.list.d/si
 ## A successfull run results in ISO file in /grml/grml_isos/ (inside container),
 ## available via volume folder also outside of docker container ($pwd/grml/grml_isos/).
 #
-### Usage instructions #############################################################################
+####################################################################################################
diff --git a/t/Dockerfile b/t/Dockerfile
index d9aef5e..50fa904 100644
--- a/t/Dockerfile
+++ b/t/Dockerfile
@@ -19,13 +19,13 @@ WORKDIR /code/
 # When you want to build the base image from scratch
 # jump to the next section if you don't want to build yourself!:
 #
-# % docker build --tag="lua-ngcp-kamailio-buster" -f t/Dockerfile .
-# % docker run --rm -i -t -v $(pwd):/code:rw lua-ngcp-kamailio-buster:latest bash
+# % docker build --tag="deployment-iso-buster" -f t/Dockerfile .
+# % docker run --rm -i -t -v $(pwd):/code:rw deployment-iso-buster:latest bash
 #
 # Use the existing docker image:
-# % docker pull docker.mgm.sipwise.com/lua-ngcp-kamailio-buster
+# % docker pull docker.mgm.sipwise.com/deployment-iso-buster
 # NOTE: run the following command from root folder of git repository:
-# % docker run --rm -i -t -v $(pwd):/code:rw docker.mgm.sipwise.com/lua-ngcp-kamailio-buster:latest bash
+# % docker run --rm -i -t -v $(pwd):/code:rw docker.mgm.sipwise.com/deployment-iso-buster:latest bash
 #
 # Inside docker (the command is in history, just press UP button):
 #   ./t/testrunner
diff --git a/t/iso-tester b/t/iso-tester
new file mode 100755
index 0000000..b4b5ea3
--- /dev/null
+++ b/t/iso-tester
@@ -0,0 +1,101 @@
+#!/bin/bash
+
+set -eu -o pipefail
+
+usage_information() {
+  local PN
+  PN="$(basename "$0")"
+
+  echo "${PN}: tool to boot an ISO, generate a screenshot of the system,"
+  echo "and optionally compare the screenshot against a reference image"
+  echo
+  echo "Usage: ${PN} <filename.iso> <screenshot.jpg> [<screenshot_compare.jpg>]" >&2
+  echo
+  echo "Usage examples:
+
+  ${PN} ./sip_provider_mr7.5.1.iso /tmp/screenshot.jpg
+
+  ${PN} ./sip_provider_mr7.5.1.iso /tmp/memtest.jpg ./t/screenshots/01-memtest.jpg
+  "
+}
+
+if [ $# -lt 2 ] ; then
+  usage_information >&2
+  exit 1
+fi
+
+ISO="$1"
+SCREENSHOT="$2"
+
+if [ -n "${3:-}" ] ; then
+  SCREENSHOT_COMPARE="$3"
+fi
+
+QEMU_MONITOR=$(mktemp)
+SCREENDUMP=$(mktemp)
+
+send_command() {
+  echo "$*" | socat - UNIX-CONNECT:"${QEMU_MONITOR}"
+  # this is necessary to give the system some time to execute it before receiving the next command
+  sleep 0.1
+}
+
+if [[ ! -x "$(which qemu-system-x86_64)" ]] || [[ ! -x "$(which socat)" ]] || [[ ! -x "$(which convert)" ]]; then
+  # only install tools automatically inside docker environment
+  if [ -e /.dockerinit ] || [ -e /.dockerenv ] ; then
+    apt-get update
+    apt-get install --assume-yes --no-install-recommends socat qemu-system-x86 imagemagick
+  else
+    echo "Please make sure to have socat qemu-system-x86 imagemagick available, not automatically installing them." >&2
+    exit 1
+  fi
+fi
+
+qemu-system-x86_64 -display none -monitor unix:"${QEMU_MONITOR}",server,nowait -boot order=d -m 128 -cdrom "${ISO}" &
+PID=$!
+echo "qemu process running as PID $PID"
+
+# ensure the qemu process is ready for receiving commands
+sleep 1
+
+send_command "sendkey down"
+send_command "sendkey down"
+send_command "sendkey down"
+send_command "sendkey down"
+send_command "sendkey down"
+send_command "sendkey down"
+send_command "sendkey ret"
+send_command "sendkey down"
+send_command "sendkey ret"
+send_command "screendump ${SCREENDUMP}"
+send_command "quit"
+
+rm -f "${QEMU_MONITOR}"
+
+if ! [ -f "${SCREENDUMP}" ] ; then
+  echo "Failed to generated screenshot file, bailing out." >&2
+  kill "$PID" || true
+  exit 1
+fi
+
+convert "${SCREENDUMP}" "${SCREENSHOT}"
+echo "Generated screenshot file ${SCREENSHOT}"
+rm -f "${SCREENDUMP}"
+
+if ! [ -x ./screenshot-compare ] ; then
+  wget -O ./screenshot-compare https://deb.sipwise.com/files/screenshot-compare
+  chmod 755 ./screenshot-compare
+fi
+
+mkdir -p reports
+if [ -n "${SCREENSHOT_COMPARE:-}" ] ; then
+  echo "Comparing ${SCREENSHOT} against ${SCREENSHOT_COMPARE}"
+  if ./screenshot-compare "${SCREENSHOT}" "${SCREENSHOT_COMPARE}" ; then
+    echo "Looks like ${SCREENSHOT} and ${SCREENSHOT_COMPARE} are looking similar enough."
+    echo "1..0" > ./reports/screenshot.tap
+  else
+    echo "Looks like ${SCREENSHOT} and ${SCREENSHOT_COMPARE} are NOT looking similar enough."
+    echo "1..1" > ./reports/screenshot.tap
+    echo "not ok 1  ${SCREENSHOT} and ${SCREENSHOT_COMPARE} don't look similar enough" >> ./reports/screenshot.tap
+  fi
+fi
diff --git a/t/screenshots/01-memtest.jpg b/t/screenshots/01-memtest.jpg
new file mode 100644
index 0000000..ddda8ec
Binary files /dev/null and b/t/screenshots/01-memtest.jpg differ
diff --git a/templates/boot/grub/addons.cfg b/templates/boot/grub/addons.cfg
new file mode 100644
index 0000000..e257de3
--- /dev/null
+++ b/templates/boot/grub/addons.cfg
@@ -0,0 +1,25 @@
+submenu "Addons ->" --class=submenu {
+  menuentry "iPXE - boot via network/PXE" {
+      if [ "${grub_platform}" == "efi" ] ; then
+        chainloader /boot/addons/ipxe.efi
+      else
+        insmod linux16
+        linux16 /boot/addons/ipxe.lkrn
+      fi
+  }
+
+  menuentry "Netboot.xyz" {
+      if [ "${grub_platform}" == "efi" ] ; then
+        chainloader /boot/addons/netboot.xyz.efi
+      else
+        insmod linux16
+        linux16 /boot/addons/netboot.xyz.lkrn
+      fi
+  }
+
+  menuentry "Boot OS of first partition on first disk" {
+      set root=(hd0,1)
+      chainloader +1
+  }
+
+}