diff --git a/docker/roundcube/rootfs/opt/app-root/src/init.sh b/docker/roundcube/rootfs/opt/app-root/src/init.sh
index 5dc289b9..6777ab92 100755
--- a/docker/roundcube/rootfs/opt/app-root/src/init.sh
+++ b/docker/roundcube/rootfs/opt/app-root/src/init.sh
@@ -1,203 +1,205 @@
 #!/bin/bash
 
 echo "Starting"
 set -e
 set -x
 
 mkdir -p /data/pgp-home
 chmod 777 /data/pgp-home
 
 pushd /opt/app-root/src/
 pushd roundcubemail
 
 ## Copy our configs over the default ones
 cp /opt/app-root/src/roundcubemail-config-templates/* config/
 
 # Initialize the db
 if [[ "$DB_ROOT_PASSWORD" == "" ]]; then
     echo "Not using password"
     cat > /tmp/kolab-setup-my.cnf << EOF
 [client]
 host=${DB_HOST}
 user=root
 EOF
 else
     cat > /tmp/kolab-setup-my.cnf << EOF
 [client]
 host=${DB_HOST}
 user=root
 password=${DB_ROOT_PASSWORD}
 EOF
 fi
 
 mysql --defaults-file=/tmp/kolab-setup-my.cnf <<EOF
 CREATE DATABASE IF NOT EXISTS $DB_RC_DATABASE CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
 CREATE USER IF NOT EXISTS $DB_RC_USERNAME@'%' IDENTIFIED BY '$DB_RC_PASSWORD';
 ALTER USER $DB_RC_USERNAME@'%' IDENTIFIED BY '$DB_RC_PASSWORD';
 GRANT ALL PRIVILEGES ON $DB_RC_DATABASE.* TO $DB_RC_USERNAME@'%';
 FLUSH PRIVILEGES;
 EOF
 
 # Run roundcube and plugin database initializations
 echo "Initializing tables..."
 bin/initdb.sh --dir SQL/ || :
 
 for plugin in $(find plugins -mindepth 1 -maxdepth 1 -type d | sort); do
     if [ ! -z "$(find ${plugin} -type d -name SQL)" ]; then
         for dir in $(find plugins/$(basename ${plugin})/ -type d -name SQL); do
             # Skip plugins with multiple drivers and no kolab driver
             if [ ! -z "$(echo $dir | grep driver)" ]; then
                 if [ -z "$(echo $dir | grep kolab)" ]; then
                     continue
                 fi
             fi
 
             bin/initdb.sh \
                 --dir $dir \
                 --package $(basename ${plugin}) \
                 >/dev/null 2>&1 || :
         done
     fi
 done
 
 popd
 
 roundcubemail/bin/initdb.sh --dir syncroton/docs/SQL/ || :
 roundcubemail/bin/initdb.sh --dir chwala/doc/SQL/ || :
 
 echo "Updating tables..."
 roundcubemail/bin/updatedb.sh --dir syncroton/docs/SQL/ --package syncroton || :
 roundcubemail/bin/updatedb.sh --dir roundcubemail/SQL/ --package roundcube || :
 roundcubemail/bin/updatedb.sh --dir roundcubemail/plugins/libkolab/SQL/ --package libkolab || :
 roundcubemail/bin/updatedb.sh --dir roundcubemail/plugins/kolab-calendar/SQL/ --package calendar-kolab || :
 
 echo ""
 echo "Done, starting httpd..."
 
 if [ "$1" == "syncroton" ]; then
     ./update-from-source.sh || :
 
     sed -i "s/?>/\$config['activesync_test_username'] = 'john@kolab.org';\n?>/" roundcubemail/config/config.inc.php
     sed -i "s/?>/\$config['activesync_test_password'] = 'simple123';\n?>/" roundcubemail/config/config.inc.php
     sed -i "s/?>/\$config['activesync_test_host'] = 'http:\/\/localhost:8001';\n?>/" roundcubemail/config/config.inc.php
     sed -i -r -e "s/config\['activesync_init_subscriptions'\] =.*$/config['activesync_init_subscriptions'] = 0;/g" roundcubemail/config/kolab_syncroton.inc.php
     sed -i -r -e "s/config\['activesync_multifolder_blacklist_event'\] =.*$/config['activesync_multifolder_blacklist_event'] = array('windowsoutlook');/g" roundcubemail/config/kolab_syncroton.inc.php
     sed -i -r -e "s/config\['activesync_multifolder_blacklist_task'\] =.*$/config['activesync_multifolder_blacklist_task'] = array('windowsoutlook');/g" roundcubemail/config/kolab_syncroton.inc.php
     sed -i -r -e "s/config\['activesync_multifolder_blacklist_contact'\] =.*$/config['activesync_multifolder_blacklist_contact'] = array('windowsoutlook');/g" roundcubemail/config/kolab_syncroton.inc.php
 
     pushd syncroton
     php -S localhost:8001 &
     pushd tests
 
     if [ "$2" == "testsuite" ]; then
         php \
             -dmemory_limit=-1 \
             ../vendor/bin/phpunit \
             --verbose \
             --testsuite Unit
         php \
             -dmemory_limit=-1 \
             ../vendor/bin/phpunit \
             --verbose \
             --testsuite Sync
     elif [ "$2" == "quicktest" ]; then
         php \
             -dmemory_limit=-1 \
             ../vendor/bin/phpunit \
             --verbose \
             --testsuite Unit
     elif [ "$2" == "lint" ]; then
         popd
+        cp ../syncroton.phpstan.neon phpstan.neon
         php -dmemory_limit=-1 vendor/bin/phpstan
     elif [ "$2" == "shell" ]; then
         exec /bin/bash
     else
         php \
             -dmemory_limit=-1 \
             ../vendor/bin/phpunit \
             --verbose \
             --stop-on-defect \
             --stop-on-error \
             --stop-on-failure \
             "$2"
     fi
 elif [ "$1" == "irony" ]; then
     ./update-from-source.sh || :
 
     pushd iRony
     pushd test
 
     if [ "$2" == "testsuite" ]; then
         php \
             -dmemory_limit=-1 \
             ../vendor/bin/phpunit \
             --verbose
     elif [ "$2" == "shell" ]; then
         exec /bin/bash
     else
         php \
             -dmemory_limit=-1 \
             ../vendor/bin/phpunit \
             --verbose \
             --stop-on-defect \
             --stop-on-error \
             --stop-on-failure \
             "$2"
     fi
 elif [ "$1" == "roundcubemail-plugins-kolab" ]; then
     ./update-from-source.sh || :
     # We run the tests from the plugins directory, which we don't normally update
     if [ -d /src.orig/roundcubemail-plugins-kolab ]; then
         rsync -av \
             --no-links \
             --exclude=vendor \
             --exclude=temp \
             --exclude=config \
             --exclude=logs \
             --exclude=.git \
             --exclude=config.inc.php \
             --exclude=composer.json \
             --exclude=composer.lock \
             /src.orig/roundcubemail-plugins-kolab/ /opt/app-root/src/roundcubemail-plugins-kolab
     fi
 
     pushd roundcubemail-plugins-kolab
     ln -s ../roundcubemail/tests tests
     ln -s ../roundcubemail/program program
 
     if [ "$2" == "testsuite" ]; then
         #FIXME this doesn't currently work:
         #* set logging to stdout
         #* add test configuration
         #* there's some error about serializing a libcalendaring object to a string?
         php \
             -dmemory_limit=-1 \
             ../roundcubemail/vendor/bin/phpunit \
             --verbose
     elif [ "$2" == "lint" ]; then
+        cp ../roundcubemail-plugins-kolab.phpstan.neon phpstan.neon
         php -dmemory_limit=-1 ../roundcubemail/vendor/bin/phpstan
         php ../roundcubemail/vendor/bin/php-cs-fixer fix --dry-run --using-cache=no --diff --verbose
     elif [ "$2" == "shell" ]; then
         exec /bin/bash
     else
         php \
             -dmemory_limit=-1 \
             ../roundcubemail/vendor/bin/phpunit \
             --verbose \
             --stop-on-defect \
             --stop-on-error \
             --stop-on-failure \
             "$2"
     fi
 elif [ "$1" == "phpstan" ]; then
     ./update-from-source.sh || :
     pushd roundcubemail
     cp /src.orig/roundcubemail-plugins-kolab/phpstan.neon .
     cp /src.orig/roundcubemail-plugins-kolab/phpstan.bootstrap.php .
     php -dmemory_limit=-1 vendor/bin/phpstan analyse
 elif [ "$1" == "shell" ]; then
     exec /bin/bash
 else
     /usr/sbin/php-fpm
     exec httpd -DFOREGROUND
 fi
diff --git a/docker/roundcube/rootfs/opt/app-root/src/roundcubemail-plugins-kolab.phpstan.neon b/docker/roundcube/rootfs/opt/app-root/src/roundcubemail-plugins-kolab.phpstan.neon
new file mode 100644
index 00000000..11d2a761
--- /dev/null
+++ b/docker/roundcube/rootfs/opt/app-root/src/roundcubemail-plugins-kolab.phpstan.neon
@@ -0,0 +1,46 @@
+parameters:
+    bootstrapFiles:
+        - phpstan.bootstrap.php
+
+    fileExtensions:
+        - php
+        - php.dist
+        - inc
+
+    ignoreErrors:
+        # TODO: These need to be fixed somehow
+        - '#Access to an undefined property (calendar|tasklist)::\$driver#'
+        - '#Access to an undefined property (calendar|tasklist)::\$itip#'
+        - '#Access to an undefined property (calendar|tasklist)::\$ical#'
+        - '#Access to an undefined property Kolab2FA\\.*::\$(username|secret)#'
+        - '#Access to an undefined property Sabre\\VObject.*::\$.*#'
+        - '#Access to an undefined property DateTime::\$_dateonly#'
+        # All expressions below are about libkolabxml (which I don't have installed)
+        - '#unknown class (kolabformat|Address|Alarm|Contact|ContactReference|Configuration|Email|RecurrenceRule|Key|Related|Url|Snippet|Telephone)#'
+        - '#class (Address|Affiliation|Alarm|Attachment|Attendee|Duration|ContactReference|Configuration|Dictionary|Email|EmailReference|DayPos|FileDriver|Key|NameComponents|SnippetCollection|RecurrenceRule|Related|Relation|EventCal|Snippet|Telephone|Url|vector.*|cDatetime|XMLObject|CustomProperty) not found#i'
+        - '#Call to method .* on an unknown class (Affiliation|Attachment|Attendee|cDateTime|Dictionary|EventCal|FileDriver|NameComponents|Relation|snippet|vector.*)#'
+        - '#invalid .*type vector#'
+        - '#(cDateTime|EventCal)#'
+
+        # Extra ignores because we run against dev/kolab-1.5
+        - '#Call to an undefined method (rcube_output::.*|rcube_storage::fetch_headers\(\)|rcube_storage::fetch_headers\(\))#'
+        - '#Class .* constructor invoked with 0 parameters, 1 required.#'
+        - '#Access to an undefined property (rcube_output::\$ajax_call|rcube_message_header::\$list_flags|object::\$body|rcube_output::\$type).#'
+        - '#Method rcube_output::send\(\) invoked with 1 parameter, 0 required.#'
+        - '#Method rcube_output::set_env\(\) invoked with 3 parameters, 2 required.#'
+        # Class unknown
+        - '#.*(Firebase\\JWT|Yubikey\\Validate|OTPHP\\HOTP|OTPHP\\TOTP).*#'
+        - '#Right side of && is always true.#'
+        - '#Class Key constructor invoked with 0 parameters, 2 required.#'
+        - '#Property rcube::\$storage \(rcube_storage\) does not accept null.#'
+
+    paths:
+        - plugins
+
+    level: 4
+
+    scanDirectories:
+        - program/include
+        - program/actions
+
+    treatPhpDocTypesAsCertain: false
diff --git a/docker/roundcube/rootfs/opt/app-root/src/syncroton.phpstan.neon b/docker/roundcube/rootfs/opt/app-root/src/syncroton.phpstan.neon
new file mode 100644
index 00000000..76db1684
--- /dev/null
+++ b/docker/roundcube/rootfs/opt/app-root/src/syncroton.phpstan.neon
@@ -0,0 +1,26 @@
+parameters:
+    bootstrapFiles:
+        - phpstan.bootstrap.php
+
+    excludePaths:
+        - vendor
+        - lib/plugins
+        - lib/ext/Roundcube
+        - lib/ext/Syncroton
+
+    ignoreErrors:
+        - |Access to an undefined property Syncroton_Model_.*|
+        # Customizations because of dev/kolab-1.5
+        #- |Access to offset .* on an unknown class An.|
+        - '#Property rcube::\$user \(rcube_user\) in empty\(\) is not falsy.#'
+
+    paths:
+        - lib
+        - tests
+
+    level: 4
+
+    scanDirectories:
+        - lib/ext
+
+    treatPhpDocTypesAsCertain: false