diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml
index 35eb914ef1faa1659ded6931b4592791404317ba..c1b74fed98facc6aa3addd8436f9fc95500e856d 100644
--- a/.gitlab-ci.yml
+++ b/.gitlab-ci.yml
@@ -12,6 +12,15 @@ default:
     - source /opt/molecule/bin/activate
     - ansible --version
     - molecule --version
+  after_script:
+    - source /opt/molecule/bin/activate
+    - molecule destroy --scenario-name ${SCENARIO}
+
+variables:
+  SCENARIO: "default"
+  ANSIBLE_VAULT_PASSWORD_FILE: "${CI_PROJECT_DIR}/molecule/${SCENARIO}/../../../lza_backup_proxy.pass"
+  ANSIBLE_FORCE_COLOR: 'true'
+  PY_COLORS: '1'
 
 test-job:
   stage: test
@@ -19,15 +28,18 @@ test-job:
     - "shell"
   script:
     # make sure that Ansible Vaults are present and can be decrypted
-    - echo "${VAULT_LZA_BACKUP_PROXY}" > ../lza_backup_proxy.pass
-    - export ANSIBLE_VAULT_IDENTITY_LIST="../lza_backup_proxy.pass"
+    - echo "${VAULT_LZA_BACKUP_PROXY}" > ${ANSIBLE_VAULT_PASSWORD_FILE}
+    - export ANSIBLE_VAULT_IDENTITY_LIST="${ANSIBLE_VAULT_PASSWORD_FILE}"
     - rm -rf ../ansible_vaults/
     - git clone https://gitlab+deploy-token-25:${VAULT_ACCESS_TOKEN}@git.slub-dresden.de/slub-referat-2-3/ansible_vaults.git ../ansible_vaults/; \
     # run Molecule tests
-    - molecule syntax --scenario-name default
-    - molecule lint --scenario-name default
-    - molecule create --scenario-name default
-    - molecule converge --scenario-name default
-    - molecule idempotence --scenario-name default
-    # - molecule verify --scenario-name default
-    - molecule destroy --scenario-name default
+    - molecule syntax --scenario-name ${SCENARIO}
+    # We cannot use `molecule lint` anymore because:
+    # - https://github.com/ansible-community/molecule/pull/3802 "Remove lint command"
+    # - https://github.com/ansible-community/molecule/discussions/3825#discussioncomment-4908366
+    - yamllint --strict --format colored ./
+    - ansible-lint --format full --profile production --strict --force-color ./
+    - molecule create --scenario-name ${SCENARIO}
+    - molecule converge --scenario-name ${SCENARIO}
+    - molecule idempotence --scenario-name ${SCENARIO}
+    # - molecule verify --scenario-name ${SCENARIO}
diff --git a/molecule/resources/playbooks/prepare.yml b/molecule/resources/playbooks/prepare.yml
index a20ecff01683308e1b648dd4fbb9ff4703258b76..b15367f5f1e116e022d1cb1f0e947c54ff4faced 100644
--- a/molecule/resources/playbooks/prepare.yml
+++ b/molecule/resources/playbooks/prepare.yml
@@ -1,22 +1,108 @@
 ---
 - name: Prepare
   hosts: "*"
-  tasks:
-    - name: install GPG
-      ansible.builtin.apt:
-        name: "gnupg"
-        state: latest
-        update_cache: true
-      become: true
-    - name: add GPG key for SLUB Debian repository
-      ansible.builtin.apt_key:
-        url: "https://sdvdebianrepo.slub-dresden.de/deb-repository/pub.gpg.key"
-        state: present
+  pre_tasks:
+    - name: configure additional package repositories for Debian
+      when: ansible_os_family == "Debian"
+      block:
+        - name: install GPG
+          ansible.builtin.apt:
+            name: "gnupg"
+            state: latest
+            update_cache: true
+          become: true
+        - name: add GPG key for SLUB Debian repository
+          ansible.builtin.apt_key:
+            url: "{{ item }}"
+            state: absent
+          loop:
+            - "http://bdv141.slub-dresden.de/deb-repository/pub.gpg.key"
+            - "https://sdvdebianrepo.slub-dresden.de/deb-repository/pub.gpg.key"
+        - name: add repo URL to sources.list
+          ansible.builtin.apt_repository:
+            repo: "{{ item }}"
+            state: absent
+          loop:
+            - "deb http://bdv141.slub-dresden.de/deb-repository lza-testing main"
+            - "deb https://sdvdebianrepo.slub-dresden.de/deb-repository bullseye main"
+        - name: modify package repo config
+          ansible.builtin.deb822_repository:
+            architectures: "amd64"
+            components: "{{ item.components | default('main') }}"
+            enabled: "{{ item.enabled | default(true) }}"
+            name: "{{ item.name }}"
+            pdiffs: true
+            signed_by: "{{ item.signed_by }}"
+            suites: "{{ item.suites | default(ansible_lsb.codename) }}"
+            uris: "{{ item.uris }}"
+          loop:
+            # PC @steidl with local Debian repo for SubAp tests
+            # - name: "bdv141"
+            #   signed_by: "http://bdv141.slub-dresden.de/deb-repository/pub.gpg.key"
+            #   suites: "lza-testing"
+            #   uris: "http://bdv141.slub-dresden.de/deb-repository"
+            # on-prem Debian Repo
+            - name: "slub"
+              signed_by: "https://sdvdebianrepo.slub-dresden.de/deb-repository/pub.gpg.key"
+              uris: "https://sdvdebianrepo.slub-dresden.de/deb-repository"
+            # add non-free repos to be able to install libmath-random-perl from official Debian public repo
+            - name: "debian"
+              components: ["main", "non-free"]
+              signed_by: "/usr/share/keyrings/debian-archive-keyring.gpg"
+              suites: ["{{ ansible_lsb.codename }}", "{{ ansible_lsb.codename }}-updates"]
+              uris: "http://deb.debian.org/debian"
+          notify: update package cache
+          become: true
+
+    - name: configure additional package repositories for RedHat
+      when: ansible_os_family == "RedHat"
+      block:
+        - name: add custom repositories
+          ansible.builtin.yum_repository:
+            name: "{{ item.name }}"
+            description: "{{ item.description }}"
+            baseurl: "{{ item.baseurl }}"
+            gpgcheck: "{{ item.gpgcheck | default('true') }}"
+            gpgkey: "{{ item.gpgkey | default(omit) }}"
+          loop:
+            - name: "epel"
+              description: EPEL YUM repo
+              baseurl: "https://download.fedoraproject.org/pub/epel/{{ ansible_distribution_major_version }}/x86_64/"
+              gpgkey: "https://dl.fedoraproject.org/pub/epel/RPM-GPG-KEY-EPEL-{{ ansible_distribution_major_version }}"
+            - name: "slub"
+              description: SLUB YUM repo
+              baseurl: "https://sdvrhelrepo.slub-dresden.de/"
+              gpgcheck: "false"
+          notify: update package cache
+        - name: remove legacy repo configuration to avoid double configuration for SLUB repo
+          ansible.builtin.file:
+            path: "/etc/yum.repos.d/SLUB.repo"
+            state: absent
+          notify: update package cache
+
+    # Ansible roles can install a multitude of firewall rules, some of which
+    # will lock us out of our Molecule test VM if we don't take precautions.
+    # As Molecule itself uses SSH just like Ansible, we need to open port
+    # tcp/22 to the private /24 subnet that Vagrant uses when provisioning the
+    # VM. As we don't know for sure what the address for this subnet is and it
+    # can change across servers/platforms, we gather this information
+    # dynamically and filter it through `ipaddr` to get the address of the
+    # whole subnet. The rule is inserted right on top of the list to make sure
+    # we always get access.
+    - name: add firewall rule to allow access from Molecule host into testing VM
+      ansible.builtin.iptables:
+        action: insert
+        rule_num: 1
+        chain: INPUT
+        comment: "molecule access"
+        jump: "ACCEPT"
+        protocol: tcp
+        source: "{{ ansible_default_ipv4.address | ansible.utils.ipaddr('network') }}/24"
+        destination_port: "22"
       become: true
-    - name: add repo URL to sources.list
-      ansible.builtin.apt_repository:
-        repo: "deb https://sdvdebianrepo.slub-dresden.de/deb-repository bullseye main"
-        state: present
+
+  handlers:
+    - name: update package cache
+      ansible.builtin.package:
         update_cache: true
-        mode: "0644"
       become: true
diff --git a/molecule/virtualbox/molecule.yml b/molecule/virtualbox/molecule.yml
index 62d3860d016d50054311138b6b6a2c68ad19945e..c1f6e5d26115c5c83714179a855aeed4e7c56b2c 100644
--- a/molecule/virtualbox/molecule.yml
+++ b/molecule/virtualbox/molecule.yml
@@ -13,7 +13,7 @@ platforms:
   # https://github.com/ansible-community/molecule-vagrant#documentation
   # for more platform parameters.
   - name: vm-runner
-    box: debian/bullseye64
+    box: debian/bookworm64
     memory: 1024
     # List of raw Vagrant `config` options.
     # provider_raw_config_args:
@@ -21,7 +21,7 @@ platforms:
     # Dictionary of `config` options.
     config_options:
       ssh.keep_alive: yes
-      ssh.remote_user: "'lza'"
+      ssh.remote_user: "lza"
 provisioner:
   name: ansible
   log: true
@@ -29,7 +29,7 @@ provisioner:
     defaults:
       # https://stackoverflow.com/questions/57435811/ansible-molecule-pass-multiple-vault-ids
       # vault_identity_list: "@$HOME/.ansible/roles/lza_install_common.pass, @$HOME/.ansible/roles/passfile_1.pass"
-      vault_identity_list: "../lza_backup_proxy.pass"
+      vault_identity_list: "../../../lza_backup_proxy.pass"
   vvv: false
   playbooks:
     # create: ../resources/playbooks/create.yml