diff --git a/.github/actions/ci-setup/action.yml b/.github/actions/ci-setup/action.yml
index 227578f5..f800cf09 100644
--- a/.github/actions/ci-setup/action.yml
+++ b/.github/actions/ci-setup/action.yml
@@ -44,5 +44,5 @@ runs:
         ini-values: pcov.directory=module
     - name: Install dependencies
       if: ${{ inputs.install-deps == 'yes' }}
-      run: composer install --no-interaction --prefer-dist
+      run: composer install --no-interaction --prefer-dist ${{ inputs.php-version == '8.4' && '--ignore-platform-req=php' || '' }}
       shell: bash
diff --git a/.github/workflows/ci-db-tests.yml b/.github/workflows/ci-db-tests.yml
index 8cea11f7..ba45e9f4 100644
--- a/.github/workflows/ci-db-tests.yml
+++ b/.github/workflows/ci-db-tests.yml
@@ -10,10 +10,11 @@ on:
 
 jobs:
   db-tests:
-    runs-on: ubuntu-22.04
+    runs-on: ubuntu-24.04
     strategy:
       matrix:
-        php-version: ['8.2', '8.3']
+        php-version: ['8.2', '8.3', '8.4']
+    continue-on-error: ${{ matrix.php-version == '8.4' }}
     env:
       LC_ALL: C
     steps:
@@ -36,7 +37,7 @@ jobs:
         run: composer test:db:${{ inputs.platform }}
       - name: Upload code coverage
         uses: actions/upload-artifact@v4
-        if: ${{ matrix.php-version == '8.2' && inputs.platform == 'sqlite:ci' }}
+        if: ${{ matrix.php-version == '8.3' && inputs.platform == 'sqlite:ci' }}
         with:
           name: coverage-db
           path: |
diff --git a/.github/workflows/ci-docker-image-build.yml b/.github/workflows/ci-docker-image-build.yml
index 43812fad..ab9681c5 100644
--- a/.github/workflows/ci-docker-image-build.yml
+++ b/.github/workflows/ci-docker-image-build.yml
@@ -7,7 +7,7 @@ on:
 
 jobs:
   build-docker-image:
-    runs-on: ubuntu-22.04
+    runs-on: ubuntu-24.04
     steps:
       - name: Checkout code
         uses: actions/checkout@v4
diff --git a/.github/workflows/ci-tests.yml b/.github/workflows/ci-tests.yml
index d2cf4d9a..70fe8049 100644
--- a/.github/workflows/ci-tests.yml
+++ b/.github/workflows/ci-tests.yml
@@ -10,10 +10,11 @@ on:
 
 jobs:
   tests:
-    runs-on: ubuntu-22.04
+    runs-on: ubuntu-24.04
     strategy:
       matrix:
-        php-version: ['8.2', '8.3']
+        php-version: ['8.2', '8.3', '8.4']
+    continue-on-error: ${{ matrix.php-version == '8.4' }}
     env:
       GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} # rr get-binary picks this env automatically
     steps:
@@ -33,7 +34,7 @@ jobs:
         run: ./vendor/bin/rr get --no-interaction --no-config --location bin/ && chmod +x bin/rr
       - run: composer test:${{ inputs.test-group }}:ci
       - uses: actions/upload-artifact@v4
-        if: ${{ matrix.php-version == '8.2' }}
+        if: ${{ matrix.php-version == '8.3' }}
         with:
           name: coverage-${{ inputs.test-group }}
           path: |
diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index 933d71b6..a93559cb 100644
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -24,10 +24,10 @@ on:
 
 jobs:
   static-analysis:
-    runs-on: ubuntu-22.04
+    runs-on: ubuntu-24.04
     strategy:
       matrix:
-        php-version: ['8.2']
+        php-version: ['8.3']
         command: ['cs', 'stan', 'swagger:validate']
     steps:
       - uses: actions/checkout@v4
@@ -66,10 +66,10 @@ jobs:
       - api-tests
       - cli-tests
       - db-tests
-    runs-on: ubuntu-22.04
+    runs-on: ubuntu-24.04
     strategy:
       matrix:
-        php-version: ['8.2']
+        php-version: ['8.3']
     steps:
       - name: Checkout code
         uses: actions/checkout@v4
@@ -94,7 +94,7 @@ jobs:
   delete-artifacts:
     needs:
       - upload-coverage
-    runs-on: ubuntu-22.04
+    runs-on: ubuntu-24.04
     steps:
       - uses: geekyeggo/delete-artifact@v2
         with:
diff --git a/.github/workflows/publish-release.yml b/.github/workflows/publish-release.yml
index 7875c07b..a81d51fb 100644
--- a/.github/workflows/publish-release.yml
+++ b/.github/workflows/publish-release.yml
@@ -7,10 +7,10 @@ on:
 
 jobs:
   build:
-    runs-on: ubuntu-22.04
+    runs-on: ubuntu-24.04
     strategy:
       matrix:
-        php-version: ['8.2', '8.3']
+        php-version: ['8.2', '8.3'] # TODO 8.4
     steps:
       - uses: actions/checkout@v4
       - uses: './.github/actions/ci-setup'
@@ -26,7 +26,7 @@ jobs:
 
   publish:
     needs: ['build']
-    runs-on: ubuntu-22.04
+    runs-on: ubuntu-24.04
     steps:
       - uses: actions/checkout@v4
       - uses: actions/download-artifact@v4
@@ -43,7 +43,7 @@ jobs:
 
   delete-artifacts:
     needs: ['publish']
-    runs-on: ubuntu-22.04
+    runs-on: ubuntu-24.04
     steps:
       - uses: geekyeggo/delete-artifact@v2
         with:
diff --git a/.github/workflows/publish-swagger-spec.yml b/.github/workflows/publish-swagger-spec.yml
index beebf57f..9607206a 100644
--- a/.github/workflows/publish-swagger-spec.yml
+++ b/.github/workflows/publish-swagger-spec.yml
@@ -7,7 +7,7 @@ on:
 
 jobs:
   build:
-    runs-on: ubuntu-22.04
+    runs-on: ubuntu-24.04
     strategy:
       matrix:
         php-version: ['8.2']