diff --git a/.github/workflows/moodle-plugin-ci.yml b/.github/workflows/moodle-plugin-ci.yml
index e57f4d327fa259049a6eed2c14058cd3f73f35ed..11680c6096605030d2f6d1a92ccc6165262397fa 100644
--- a/.github/workflows/moodle-plugin-ci.yml
+++ b/.github/workflows/moodle-plugin-ci.yml
@@ -4,7 +4,7 @@ on: [push, pull_request]
 
 jobs:
   test:
-    runs-on: ubuntu-latest
+    runs-on: ubuntu-22.04
 
     services:
       postgres:
@@ -15,11 +15,14 @@ jobs:
         ports:
           - 5432:5432
         options: --health-cmd pg_isready --health-interval 10s --health-timeout 5s --health-retries 3
+
       mariadb:
-        image: mariadb:10.6
+        image: mariadb:10
         env:
           MYSQL_USER: 'root'
           MYSQL_ALLOW_EMPTY_PASSWORD: "true"
+          MYSQL_CHARACTER_SET_SERVER: "utf8mb4"
+          MYSQL_COLLATION_SERVER: "utf8mb4_unicode_ci"
         ports:
           - 3306:3306
         options: --health-cmd="mysqladmin ping" --health-interval 10s --health-timeout 5s --health-retries 3
@@ -96,6 +99,8 @@ jobs:
           php-version: ${{ matrix.php }}
           extensions: ${{ matrix.extensions }}
           ini-values: max_input_vars=5000
+          # If you are not using code coverage, keep "none". Otherwise, use "pcov" (Moodle 3.10 and up) or "xdebug".
+          # If you try to use code coverage with "none", it will fallback to phpdbg (which has known problems).
           coverage: none
 
       - name: Initialise moodle-plugin-ci
@@ -104,11 +109,11 @@ jobs:
           echo $(cd ci/bin; pwd) >> $GITHUB_PATH
           echo $(cd ci/vendor/bin; pwd) >> $GITHUB_PATH
           sudo locale-gen en_AU.UTF-8
-          echo "NVM_DIR=$HOME/.nvm" >> $GITHUB_ENV
+          # echo "NVM_DIR=$HOME/.nvm" >> $GITHUB_ENV
+          curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.39.7/install.sh | bash
 
       - name: Install moodle-plugin-ci
-        run: |
-          moodle-plugin-ci install --plugin ./plugin --db-host=127.0.0.1
+        run: moodle-plugin-ci install --plugin ./plugin --db-host=127.0.0.1
         env:
           DB: ${{ matrix.database }}
           MOODLE_BRANCH: ${{ matrix.moodle-branch }}
@@ -117,42 +122,56 @@ jobs:
           PHPDOCCHECKER_IGNORE_PATHS: classes/vendor
 
       - name: PHP Lint
-        if: ${{ always() }}
-        run: moodle-plugin-ci phplint
+        if: ${{ !cancelled() }}
+        run: moodle-plugin-ci phplint ./plugin
 
       - name: PHP Mess Detector
         continue-on-error: true # This step will show errors but will not fail
-        if: ${{ always() }}
-        run: moodle-plugin-ci phpmd
+        if: ${{ !cancelled() }}
+        run: moodle-plugin-ci phpmd ./plugin
 
       - name: Moodle Code Checker
-        if: ${{ always() }}
-        run: moodle-plugin-ci codechecker --max-warnings 0 || true
+        if: ${{ !cancelled() }}
+        run: moodle-plugin-ci codechecker --max-warnings 0 ./plugin || true
 
       - name: Moodle PHPDoc Checker
-        if: ${{ always() }}
-        run: moodle-plugin-ci phpdoc || true
+        if: ${{ !cancelled() }}
+        run: moodle-plugin-ci phpdoc --max-warnings 0 ./plugin || true
 
       - name: Validating
-        if: ${{ always() }}
-        run: moodle-plugin-ci validate
+        if: ${{ !cancelled() }}
+        run: moodle-plugin-ci validate ./plugin
 
       - name: Check upgrade savepoints
-        if: ${{ always() }}
-        run: moodle-plugin-ci savepoints
+        if: ${{ !cancelled() }}
+        run: moodle-plugin-ci savepoints ./plugin
 
       - name: Mustache Lint
-        if: ${{ always() }}
-        run: moodle-plugin-ci mustache || true
+        if: ${{ !cancelled() }}
+        run: moodle-plugin-ci mustache ./plugin || true
 
       - name: Grunt
-        if: ${{ always() }}
-        run: moodle-plugin-ci grunt || true
+        if: ${{ !cancelled() }}
+        run: moodle-plugin-ci grunt ./plugin || true
 
       - name: PHPUnit tests
-        if: ${{ always() }}
-        run: moodle-plugin-ci phpunit --coverage-text || true
+        if: ${{ !cancelled() }}
+        run: moodle-plugin-ci phpunit --coverage-text ./plugin || true
 
       - name: Behat features
-        if: ${{ always() }}
-        run: moodle-plugin-ci behat --profile chrome
+        id: behat
+        if: ${{ !cancelled() }}
+        run: moodle-plugin-ci behat --profile chrome ./plugin
+
+      - name: Upload Behat Faildump
+        if: ${{ failure() && steps.behat.outcome == 'failure' }}
+        uses: actions/upload-artifact@v4
+        with:
+          name: Behat Faildump (${{ join(matrix.*, ', ') }})
+          path: ${{ github.workspace }}/moodledata/behat_dump
+          retention-days: 7
+          if-no-files-found: ignore
+
+      - name: Mark cancelled jobs as failed.
+        if: ${{ cancelled() }}
+        run: exit 1