diff --git a/lmn-www-server.yml b/lmn-www-server.yml
new file mode 100644
index 0000000..fb751fc
--- /dev/null
+++ b/lmn-www-server.yml
@@ -0,0 +1,43 @@
+## This playbook deploys a FvS web server machine.
+---
+- name: apply configuration to the web server
+  hosts: all
+  remote_user: ansible
+  become: yes
+  pre_tasks:
+    - pause:
+        prompt: "Enter global-admin AD password. Leave empty to skip domain join"
+        echo: false
+      register: adpw
+      no_log: true
+      when: "ansible_cmdline.adpw is not defined"
+  vars:
+    domain: "pn.steinbeis.schule"
+    extra_pkgs:
+      - vim
+      - apache2
+      - python3-flask
+
+    extra_pkgs_bpo: [ ]  # [ libreoffice ]
+
+  roles:
+    - up2date_debian
+    - lmn_sssd
+    - kerberize
+
+  tasks:
+    - name: Override home dir location
+      lineinfile:
+        dest: /etc/sssd/sssd.conf
+        line: override_homedir = /home/%u
+
+    - name: enable pam_mkhomedir.so
+      lineinfile:
+        dest: /etc/pam.d/common-session
+        line: "session	optional	pam_mkhomedir.so  umask=0026"
+        insertbefore: "session	optional	pam_mount.so"
+
+    - name: enable apache mod userdir
+      apache2_module:
+        state: present
+        name: userdir
diff --git a/roles/kerberize/tasks/main.yml b/roles/kerberize/tasks/main.yml
index 25ec4db..ee17a3e 100644
--- a/roles/kerberize/tasks/main.yml
+++ b/roles/kerberize/tasks/main.yml
@@ -1,28 +1,27 @@
-- name: kerberize sshd server
-  lineinfile:
-    dest: /etc/ssh/sshd_config
-    line: "GSSAPIAuthentication yes"
-    insertafter: "#GSSAPIAuthentication no"
+- name: Install kerberos packages
+  apt:
+    name: krb5-user
+    state: latest
+
+- name: Kerberize sshd server
+  ansible.builtin.copy:
+    dest: /etc/ssh/sshd_config.d/kerberize.conf
+    content: |
+      GSSAPIAuthentication yes
   notify: "reload sshd"
 
-- name: kerberize ssh client, authenticate
-  lineinfile:
-    dest: /etc/ssh/ssh_config
-    line: "GSSAPIAuthentication yes"
-    insertafter: "#   GSSAPIAuthentication no"
+- name: Kerberize ssh client, authenticate and delegate credentials
+  ansible.builtin.copy:
+    dest: /etc/ssh/ssh_config.d/kerberize.conf
+    content: |
+      GSSAPIAuthentication yes
+      GSSAPIDelegateCredentials yes
 
-- name: kerberize ssh client, delegate credentials
-  lineinfile:
-    dest: /etc/ssh/ssh_config
-    line: "GSSAPIDelegateCredentials yes"
-    insertafter: "#   GSSAPIDelegateCredentials no"
-
-
-- name: check if firefox is available
+- name: Check if firefox is available
   stat: path=/etc/firefox-esr/firefox-esr.js
   register: firefox
 
-- name: kerberize firefox for sites in the local domain
+- name: Kerberize firefox for sites in the local domain
   lineinfile:
     dest: /etc/firefox-esr/firefox-esr.js
     line: "{{ item }}"
@@ -32,12 +31,12 @@
     - 'pref("network.negotiate-auth.trusted-uris", "{{ kerberize_uris | default(ansible_domain) }}");'
   when: firefox.stat.exists
 
-- name: ensures /etc/chromium/policies/managed dir exists
-  file: 
+- name: Ensures /etc/chromium/policies/managed dir exists
+  file:
     path: "/etc/chromium/policies/managed"
     state: directory
 
-- name: kerberize chromium for sites in the local domain
+- name: Kerberize chromium for sites in the local domain
   copy:
     dest: /etc/chromium/policies/managed/idam.json
     content: |
diff --git a/roles/lmn_fvs/tasks/main.yml b/roles/lmn_fvs/tasks/main.yml
index adc3357..afffe10 100644
--- a/roles/lmn_fvs/tasks/main.yml
+++ b/roles/lmn_fvs/tasks/main.yml
@@ -50,6 +50,7 @@
       - pulseview
       - python3-websockets
       - qpdfview
+      - shellcheck
       - sigrok
       - sigrok-cli
       - tmux
@@ -64,6 +65,12 @@
   environment:
     http_proxy: '' # this is needed to avoid ttf-mscorefonts-installer picking up aptcacher
 
+- name: Remove update notifications from plasma-discover
+  apt:
+    name:
+      - plasma-discover
+    autoremove: true
+    state: absent
 
 - name: Make sure wireshark works for all users after installation and upgrades
   ansible.builtin.copy:
diff --git a/roles/lmn_vm/files/vm-netboot b/roles/lmn_vm/files/vm-netboot
new file mode 100755
index 0000000..dbb13bd
--- /dev/null
+++ b/roles/lmn_vm/files/vm-netboot
@@ -0,0 +1,61 @@
+#!/usr/bin/bash
+#
+# Start a netboot VM connected to macvtap device and fraction of mem/cpus
+#
+set -eu
+
+menu=(standard "CLI Standard Debian GNU/Linux"
+      kde-desktop "KDE Plasma Desktop Debian GNU/Linux"
+      gnome-desktop "Gnome Desktop Debian GNU/Linux")
+img=$(dialog --clear --backtitle "Virtual Machine Chooser" \
+             --title "Choose the Virtual Machine to Start" \
+             --menu "Start VM:" 12 60 6 \
+             "${menu[@]}" 2>&1 >/dev/tty)
+
+if [[ -z $img ]] ; then
+    echo "Starting VM canceled."
+    exit 1
+fi
+
+mac="$(ip link | grep -A1 "vm-macvtap" | \
+          sed -nE "s%\s+link/ether ([[:xdigit:]:]{17}) .+%\1%p")"
+
+if [[ $# -eq 0 ]] ; then
+    mem=$(sed -En "s/^MemTotal:\s+([0-9]+)\s+kB/\1/p" /proc/meminfo)
+    cpu=$(sed -En "0,/^cpu cores/s/^cpu cores\s+:\s+([0-9]+)/\1/p" /proc/cpuinfo)
+    arg=("--memory=$((mem/2048))" "--vcpu=$((cpu/2))")
+    echo Set options: "${arg[@]}"
+else
+    arg=("$@")
+fi
+
+kernel="http://livebox/d-i/n-live/$img/live/vmlinuz"
+initrd="http://livebox/d-i/n-live/$img/live/initrd.img"
+kargs=(boot=live components splash locales=de_DE.UTF-8 keyboard-layouts=de \
+                 swap=true live-config.timezone=Europe/Berlin netboot=nfs \
+                 "nfsroot=10.190.1.2:/srv/nfs/debian-live/$img/")
+
+type="ethernet,mac=${mac},target.dev=vm-macvtap,xpath1.set=./target/@managed=no"
+
+## FIXME: use passt, needs more settings for correct DNS/gateway
+# type=user,xpath1.create=./backend,xpath2.set=./backend/@type=passt,xpath3.create=./ip,xpath4.set=./ip/@family=ipv4,xpath5.set=./ip/@address=172.16.1.1,xpath6.set=./ip/@prefix=24,xpath7.create=./portForward,xpath8.set=./portForward/@proto=tcp,xpath9.set=./portForward/range/@start=2001,xpath10.set=./portForward/range/@end=2500,xpath11.set=./portForward/range/@to=1
+
+case "$img" in
+    standard)
+	arg+=("--autoconsole=text")
+	kargs+=("console=ttyS0")
+	;;
+    *)
+	;;
+esac
+
+http_proxy='' XDG_CONFIG_HOME="/tmp/${UID}/.config" \
+          exec virt-install \
+          --name "$img" \
+          --osinfo debiantesting \
+          --nodisks --import --noreboot --transient \
+          --install kernel="$kernel",initrd="$initrd",kernel_args="${kargs[*]}" \
+          --network "type=$type" "${arg[@]}"
+
+# --filesystem "$HOME",share
+# mount -t 9p share /mnt
diff --git a/roles/lmn_vm/tasks/main.yml b/roles/lmn_vm/tasks/main.yml
index d3630b7..893e8fd 100644
--- a/roles/lmn_vm/tasks/main.yml
+++ b/roles/lmn_vm/tasks/main.yml
@@ -17,6 +17,7 @@
       - mktorrent
       - libvirt-daemon-system
       - virt-manager
+      - dialog # for vm-netboot menu
     state: latest
     autoremove: true
 
@@ -264,3 +265,17 @@
     src: sync.desktop
     dest: /usr/local/share/applications/
   notify: Run update-desktop-database
+
+- name: Start virt-manager in session mode by default
+  ansible.builtin.copy:
+    dest: /usr/local/bin/virt-manager
+    content: |
+      #!/usr/bin/sh
+      exec /usr/bin/virt-manager --connect qemu:///session $@
+    mode: '0755'
+
+- name: Copy vm-netboot script
+  ansible.builtin.copy:
+    src: vm-netboot
+    dest: /usr/local/bin/
+    mode: '0755'