diff --git a/.forgejo/cascading-pr-end-to-end b/.forgejo/cascading-pr-end-to-end
new file mode 100755
index 0000000000..975888b245
--- /dev/null
+++ b/.forgejo/cascading-pr-end-to-end
@@ -0,0 +1,21 @@
+#!/bin/bash
+
+set -ex
+
+end_to_end=$1
+end_to_end_pr=$2
+forgejo=$3
+forgejo_pr=$4
+
+head_url=$(jq --raw-output .head.repo.html_url < $forgejo_pr)
+test "$head_url" != null
+branch=$(jq --raw-output .head.ref < $forgejo_pr)
+test "$branch" != null
+cd $end_to_end
+echo $head_url $branch 7.0.0+0-gitea-1.22.0 > forgejo/sources/1.22
+date > last-upgrade
+
+base_url=$(jq --raw-output .base.repo.html_url < $forgejo_pr)
+test "$base_url" != null
+test "$GITHUB_RUN_NUMBER"
+echo $base_url/actions/runs/$GITHUB_RUN_NUMBER/artifacts/forgejo > forgejo/binary-url
diff --git a/.forgejo/workflows/cascade-setup-end-to-end.yml b/.forgejo/workflows/cascade-setup-end-to-end.yml
new file mode 100644
index 0000000000..345e0db223
--- /dev/null
+++ b/.forgejo/workflows/cascade-setup-end-to-end.yml
@@ -0,0 +1,68 @@
+# SPDX-License-Identifier: MIT
+on:
+   pull_request_target:
+     types:
+       - labeled
+
+jobs:
+  info:
+    runs-on: docker
+    container:
+      image: node:20-bookworm
+    steps:
+      - name: event
+        run: |
+          env
+          echo github.event.pull_request.head.repo.fork = ${{ github.event.pull_request.head.repo.fork }}
+          echo github.event.action = ${{ github.event.action }}
+          echo github.event.pull_request.merged = ${{ github.event.pull_request.merged }}
+          echo github.event.pull_request.labels.*.name
+          cat <<'EOF'
+          ${{ toJSON(github.event.pull_request.labels.*.name) }}
+          EOF
+
+  build:
+    if: github.event.action == 'label_updated' && contains(github.event.pull_request.labels.*.name, 'run-end-to-end-tests')
+    runs-on: docker
+    container:
+      image: 'docker.io/node:20-bookworm'
+    steps:
+      - uses: https://code.forgejo.org/actions/checkout@v3
+      - uses: https://code.forgejo.org/actions/setup-go@v4
+        with:
+          go-version: ">=1.21"
+      - run: |
+          git config --add safe.directory '*'
+          adduser --quiet --comment forgejo --disabled-password forgejo
+          chown -R forgejo:forgejo .
+      - run: |
+          su forgejo -c 'make deps-backend'
+      - run: |
+          su forgejo -c 'make generate-backend static-executable && ln gitea forgejo' forgejo
+        env:
+          TAGS: bindata sqlite sqlite_unlock_notify
+      - uses: actions/upload-artifact@v3
+        with:
+          name: forgejo
+          path: forgejo
+
+  cascade:
+    if: github.event.action == 'label_updated' && contains(github.event.pull_request.labels.*.name, 'run-end-to-end-tests')
+    runs-on: docker
+    container:
+      image: node:20-bookworm
+    steps:
+      - uses: actions/checkout@v4
+      - uses: actions/cascading-pr@v1
+        with:
+          origin-url: ${{ env.GITHUB_SERVER_URL }}
+          origin-repo: ${{ github.repository }}
+          origin-token: ${{ secrets.END_TO_END_CASCADING_PR_ORIGIN }}
+          origin-pr: ${{ github.event.pull_request.number }}
+          destination-url: https://code.forgejo.org
+          destination-fork-repo: cascading-pr/end-to-end
+          destination-repo: forgejo/end-to-end
+          destination-branch: main
+          destination-token: ${{ secrets.END_TO_END_CASCADING_PR_DESTINATION }}
+          close-merge: true
+          update: .forgejo/cascading-pr-end-to-end