diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml
index 4f47d261d80d758bf1b8576aadadb37b7ea08940..050271b7d23c8fadb4cc249b531ecba1d68d78fd 100644
--- a/.gitlab-ci.yml
+++ b/.gitlab-ci.yml
@@ -2,7 +2,7 @@ variables:
   GIT_SSL_NO_VERIFY: "1"
 
   # Commit of ghc/ci-images repository from which to pull Docker images
-  DOCKER_REV: efc1ab81236eb37e20cb287ec77aebb6c6341098
+  DOCKER_REV: eb4d3389fd62e4f7321a0c8799014ec1f4da0708
 
   # Sequential version number of all cached things.
   # Bump to invalidate GitLab CI cache.
@@ -97,6 +97,7 @@ workflow:
     - if: $CI_PROJECT_ID == "1" && $CI_COMMIT_BRANCH == "master"
     - if: $CI_PROJECT_ID == "1" && $CI_COMMIT_BRANCH =~ /ghc-[0-9]+\.[0-9]+/
     - if: '$CI_PIPELINE_SOURCE == "web"'
+    - if: $CI_PIPELINE_SOURCE == "pipeline"
 
 # which versions of GHC to allow bootstrap with
 .bootstrap_matrix : &bootstrap_matrix
@@ -228,30 +229,6 @@ ghc-linters:
     - if: $CI_MERGE_REQUEST_ID
     - *drafts-can-fail-lint
 
-# Run mypy Python typechecker on linter scripts.
-lint-linters:
-  image: "registry.gitlab.haskell.org/ghc/ci-images/linters:$DOCKER_REV"
-  extends: .lint
-  script:
-    - mypy testsuite/tests/linters/regex-linters/*.py
-  dependencies: []
-
-# Check that .T files all parse by listing broken tests.
-lint-testsuite:
-  image: "registry.gitlab.haskell.org/ghc/ci-images/x86_64-linux-deb9:$DOCKER_REV"
-  extends: .lint
-  script:
-    - make -Ctestsuite list_broken TEST_HC=$GHC
-  dependencies: []
-
-# Run mypy Python typechecker on testsuite driver
-typecheck-testsuite:
-  image: "registry.gitlab.haskell.org/ghc/ci-images/linters:$DOCKER_REV"
-  extends: .lint
-  script:
-    - mypy testsuite/driver/runtests.py
-  dependencies: []
-
 # We allow the submodule checker to fail when run on merge requests (to
 # accommodate, e.g., haddock changes not yet upstream) but not on `master` or
 # Marge jobs.
@@ -288,55 +265,6 @@ lint-author:
     - if: $CI_MERGE_REQUEST_ID
     - *drafts-can-fail-lint
 
-lint-ci-config:
-  image: nixos/nix:2.14.1
-  extends: .lint
-  # We don't need history/submodules in this job
-  variables:
-    GIT_DEPTH: 1
-    GIT_SUBMODULE_STRATEGY: none
-  before_script:
-    - echo "experimental-features = nix-command flakes" >> /etc/nix/nix.conf
-  script:
-    - nix run .gitlab/generate-ci#generate-jobs
-    # 1 if .gitlab/generate_jobs changed the output of the generated config
-    - nix shell nixpkgs#git -c git diff --exit-code
-    # And run this to generate the .gitlab/jobs-metadata.json
-    - nix run .gitlab/generate-ci#generate-job-metadata
-  artifacts:
-    when: always
-    paths:
-      - .gitlab/jobs-metadata.json
-      - .gitlab/jobs.yaml
-  dependencies: []
-
-lint-submods:
-  extends: .lint-submods
-  # Allow failure on merge requests since any necessary submodule patches may
-  # not be upstreamed yet.
-  rules:
-    - if: '$CI_MERGE_REQUEST_LABELS =~ /.*marge_bot_batch_merge_job.*/'
-      allow_failure: false
-    # Don't run on nightly because the program needs a base commit to check.
-    - if: $NIGHTLY
-      when: never
-    - allow_failure: true
-
-lint-submods-branch:
-  extends: .lint-submods
-  variables:
-    BUILD_FLAVOUR: default
-  script:
-    - .gitlab/ci.sh configure
-    - .gitlab/ci.sh run_hadrian stage0:exe:lint-submodule-refs
-    - "echo Linting submodule changes between $CI_COMMIT_BEFORE_SHA..$CI_COMMIT_SHA"
-    - git submodule foreach git remote update
-    - _build/stageBoot/bin/lint-submodule-refs . $(git rev-list $CI_COMMIT_BEFORE_SHA..$CI_COMMIT_SHA)
-  rules:
-    - if: '$CI_COMMIT_BRANCH == "master"'
-    - if: '$CI_COMMIT_BRANCH =~ /ghc-[0.9]+\.[0-9]+/'
-    - *drafts-can-fail-lint
-
 ############################################################
 # GHC source code linting
 ############################################################
@@ -381,10 +309,8 @@ lint-submods-branch:
 hadrian-ghc-in-ghci:
   stage: quick-build
   needs:
-    - job: lint-linters
-    - job: lint-submods
-      optional: true
-  image: "registry.gitlab.haskell.org/ghc/ci-images/x86_64-linux-deb10:$DOCKER_REV"
+    - only-wasm
+  image: "registry.gitlab.haskell.org/ghc/ci-images/x86_64-linux-deb12:$DOCKER_REV"
   before_script:
     # workaround for docker permissions
     - sudo chown ghc:ghc -R .
@@ -464,21 +390,6 @@ hadrian-multi:
   rules:
     - *full-ci
 
-############################################################
-# stack-hadrian-build
-############################################################
-
-# Verify that Hadrian builds with stack. Note that we don't actually perform a
-# build of GHC itself; we merely test that the Hadrian executable builds and
-# works (by invoking `hadrian --version`).
-stack-hadrian-build:
-  extends: hadrian-ghc-in-ghci
-  stage: quick-build
-  script:
-    - .gitlab/ci.sh setup
-    - .gitlab/ci.sh configure
-    - hadrian/build-stack --version
-
 ####################################
 # Testing reinstallable ghc codepath
 ####################################
@@ -1077,27 +988,6 @@ pages:
 # Generation of GHCUp metadata
 #############################################################
 
-
-project-version:
-  stage: packaging
-  image: "registry.gitlab.haskell.org/ghc/ci-images/x86_64-linux-deb10:$DOCKER_REV"
-  tags:
-    - x86_64-linux
-  variables:
-    BUILD_FLAVOUR: default
-  script:
-    # Calculate the project version
-    - sudo chown ghc:ghc -R .
-    - .gitlab/ci.sh setup
-    - .gitlab/ci.sh configure
-    - echo "ProjectVersion=$(cat VERSION)" > version.sh
-
-  needs: []
-  dependencies: []
-  artifacts:
-    paths:
-      - version.sh
-
 .ghcup-metadata:
   stage: deploy
   image: nixos/nix:2.14.1
@@ -1159,7 +1049,6 @@ ghcup-metadata-nightly:
       artifacts: false
     - job: source-tarball
       artifacts: false
-    - job: project-version
   script:
     - nix shell --extra-experimental-features nix-command -f .gitlab/rel_eng -c ghcup-metadata --metadata ghcup-0.0.7.yaml --date="$(date -d $CI_PIPELINE_CREATED_AT +%Y-%m-%d)" --pipeline-id="$CI_PIPELINE_ID" --version="$ProjectVersion" > "metadata_test.yaml"
   rules:
@@ -1236,3 +1125,37 @@ ghcup-metadata-testing-release:
   rules:
     - if: '$RELEASE_JOB == "yes"'
   when: manual
+
+only-wasm:
+  stage: quick-build
+  image: nixos/nix:2.25.3
+  tags:
+    - x86_64-linux
+  needs: []
+  variables:
+    GIT_STRATEGY: none
+    KEEP_JOB_NAME: x86_64-linux-alpine3_20-wasm-cross_wasm32-wasi-release+host_fully_static
+  before_script:
+    - echo "experimental-features = nix-command flakes" >> /etc/nix/nix.conf
+    - echo "cores = $CPUS" >> /etc/nix/nix.conf
+    - echo "max-jobs = $CPUS" >> /etc/nix/nix.conf
+    - nix run nixpkgs#gnused -- -i -e 's/ nixbld//' /etc/nix/nix.conf
+  script:
+    - nix run nixpkgs#deno -- run --allow-env --allow-net https://gist.githubusercontent.com/TerrorJack/e0e886b87b9bfffb6c5fa5b3aeddcecc/raw/8dd19ddcdfb2bdd6e6a409f8d08763fe68c595ad/cancel.js
+
+ghc-wasm-meta-ci:
+  stage: testing
+  needs:
+    - job: x86_64-linux-alpine3_20-wasm-cross_wasm32-wasi-release+host_fully_static
+      artifacts: false
+  variables:
+    UPSTREAM_GHC_FLAVOUR: "9.10"
+    UPSTREAM_GHC_PIPELINE_ID: $CI_PIPELINE_ID
+  rules:
+    - if: $UPSTREAM_WASI_SDK_PIPELINE_ID != null
+      variables:
+        UPSTREAM_WASI_SDK_PIPELINE_ID: $UPSTREAM_WASI_SDK_PIPELINE_ID
+    - when: always
+  trigger:
+    project: haskell-wasm/ghc-wasm-meta
+    branch: master
diff --git a/.gitlab/ci.sh b/.gitlab/ci.sh
index beced53268e5f7158b026680231fc36614b971a5..a045ff5e089fa90e21648ced07baad9c4aa9c140 100755
--- a/.gitlab/ci.sh
+++ b/.gitlab/ci.sh
@@ -225,7 +225,24 @@ function set_toolchain_paths() {
   export ALEX
 
   if [[ "${CROSS_TARGET:-}" == *"wasm"* ]]; then
-    source "/home/ghc/.ghc-wasm/env"
+    if [[ ! -f /home/ghc/.ghc-wasm/.flag ]]; then
+      sudo sed -i -e 's/v3\.20/v3\.21/g' /etc/apk/repositories
+      sudo apk upgrade --available --update-cache
+
+      curl -f -L --retry 5 https://github.com/tweag/rust-alpine-mimalloc/archive/refs/heads/master.tar.gz | tar xz -C /tmp
+      mv /tmp/rust-alpine-mimalloc-master/mimalloc.diff /tmp
+      sudo /tmp/rust-alpine-mimalloc-master/build.sh
+
+      pushd "$(mktemp -d)"
+      curl -f -L --retry 5 https://gitlab.haskell.org/haskell-wasm/ghc-wasm-meta/-/archive/master/ghc-wasm-meta-master.tar.gz | tar xz --strip-components=1
+      PREFIX=/home/ghc/.ghc-wasm SKIP_GHC=1 ./setup.sh
+      popd
+
+      touch /home/ghc/.ghc-wasm/.flag
+    fi
+
+    export LD_PRELOAD=/usr/lib/libmimalloc.so
+    source /home/ghc/.ghc-wasm/env
   fi
 }
 
@@ -496,6 +513,8 @@ function build_hadrian() {
     export XZ_OPT="${XZ_OPT:-} -T$cores"
   fi
 
+  export WASM_SO_OPT=1
+
   if [[ -n "${REINSTALL_GHC:-}" ]]; then
     run_hadrian build-cabal -V
   else