diff --git a/.devcontainer/VERSION_MATRIX.md b/.devcontainer/VERSION_MATRIX.md
new file mode 100644
index 0000000000000000000000000000000000000000..1bf46d97e0131de797c2e351b7761a0dc72a54d6
--- /dev/null
+++ b/.devcontainer/VERSION_MATRIX.md
@@ -0,0 +1,14 @@
+# Version Matrix
+
+## Legacy
+
+| GHC   | Cabal[^1] | Stack[^2]             | HLS                           |
+|:------|:----------|:----------------------|:----------------------------- |
+| 9.6.2 | 3.10.1.0  | 2.11.1[^1]<br>2.9.3.1 | 2.2.0.0<br>2.1.0.0<br>2.0.0.1 |
+| 9.4.7 | 3.8.1.0   | 2.11.1[^1]<br>2.9.3.1 | 2.2.0.0<br>2.1.0.0<br>2.0.0.1 |
+| 9.4.6 | 3.8.1.0   | 2.11.1[^1]<br>2.9.3.1 | 2.1.0.0<br>2.0.0.1            |
+| 9.4.5 | 3.8.1.0   | 2.11.1[^1]<br>2.9.3.1 | 2.1.0.0<br>2.0.0.1            |
+| 9.2.8 | 3.6.2.0   | 2.11.1[^1]<br>2.9.3.1 | 2.2.0.0<br>2.1.0.0<br>2.0.0.1 |
+
+[^1]: built-in version; see [GHC musl > Version Matrix](../VERSION_MATRIX.md)
+[^2]: unsupported build; *statically linked* binary
diff --git a/.devcontainer/ghc-9.2/devcontainer.json b/.devcontainer/ghc-9.2/devcontainer.json
index a8aeb31a827f9bd7ff8068d82385a035d582595e..9b8da7af43c08e95ec5e58af6f4d9631d2d50593 100644
--- a/.devcontainer/ghc-9.2/devcontainer.json
+++ b/.devcontainer/ghc-9.2/devcontainer.json
@@ -1,7 +1,7 @@
 {
   "name": "GHC 9.2 (recommended)",
   "build": {
-    "dockerfile": "../GHC.Dockerfile",
+    "dockerfile": "../GHC-legacy.Dockerfile",
     "context": "..",
     "args": {
       "GHC_VERSION": "9.2",
diff --git a/.devcontainer/ghc-9.4/devcontainer.json b/.devcontainer/ghc-9.4/devcontainer.json
index c9c5c75852342f29f026ccdd5992dd3333d784e0..5b31bce212ae46a6e3f6b8811b330ab05205facc 100644
--- a/.devcontainer/ghc-9.4/devcontainer.json
+++ b/.devcontainer/ghc-9.4/devcontainer.json
@@ -5,7 +5,7 @@
     "context": "..",
     "args": {
       "GHC_VERSION": "9.4",
-      "HLS_VERSION": "2.2.0.0",
+      "HLS_VERSION": "2.3.0.0",
       "USE_ZSH_FOR_ROOT": "unset-to-use-ash",
       "SET_LANG": "C.UTF-8",
       "SET_TZ": ""
diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml
index 5b01dc6e6210502a354c1b5d803290222d39816c..0b89d547c3b322e902e39da31345b03b09bd4741 100644
--- a/.gitlab-ci.yml
+++ b/.gitlab-ci.yml
@@ -1,11 +1,11 @@
 .env:
   variables:
-    GHC_VERSION_BUILD: "9.4.7"
-    CABAL_VERSION_BUILD: "3.8.1.0"
-    STACK_VERSION: "2.13.1"
-    HLS_VERSION: "2.3.0.0"
+    GHC_VERSION_BUILD: "9.6.2"
+    CABAL_VERSION_BUILD: "3.10.1.0"
+    STACK_VERSION: "2.11.1"
+    HLS_VERSION: "2.2.0.0"
     BUILD_GHC: "true"
-    BUILD_HLS: "true"
+    BUILD_HLS: "false"
 
 .before-script-build: &before-script-build
   - |
@@ -223,13 +223,6 @@ build-manifest:version-linux-multiarch:
           $CI_APP_REPO:$CI_APP_TAG-linux-arm64v8
       fi
       docker manifest push $CI_APP_REPO:$CI_APP_TAG
-    - |
-      docker manifest create $CI_APP_REPO:$VERSION_MAJ_MIN $CI_MANIFEST_LIST
-      if echo $OS_ARCH | grep "linux-arm64v8" >/dev/null ; then
-        docker manifest annotate --variant v8 $CI_APP_REPO:$VERSION_MAJ_MIN \
-          $CI_APP_REPO:$CI_APP_TAG-linux-arm64v8
-      fi
-      docker manifest push $CI_APP_REPO:$VERSION_MAJ_MIN
   rules:
     - if: $BUILD_GHC == "true" && $CI_COMMIT_BRANCH == "freeze-version"
       changes:
@@ -380,13 +373,6 @@ build-package-hls-manifest:version-linux-multiarch:
           $CI_APP_REPO/$CI_APP_TAG/hls:$HLS_VERSION-linux-arm64v8
       fi
       docker manifest push $CI_APP_REPO/$CI_APP_TAG/hls:$HLS_VERSION
-    - |
-      docker manifest create $CI_APP_REPO/$VERSION_MAJ_MIN/hls:$HLS_VERSION $CI_MANIFEST_LIST
-      if echo $OS_ARCH | grep "linux-arm64v8" >/dev/null ; then
-        docker manifest annotate --variant v8 $CI_APP_REPO/$VERSION_MAJ_MIN/hls:$HLS_VERSION \
-          $CI_APP_REPO/$CI_APP_TAG/hls:$HLS_VERSION-linux-arm64v8
-      fi
-      docker manifest push $CI_APP_REPO/$VERSION_MAJ_MIN/hls:$HLS_VERSION
   rules:
     - if: $BUILD_HLS == "true" && $CI_COMMIT_BRANCH == "freeze-version"
       changes:
@@ -526,12 +512,5 @@ update-manifest:version-linux-multiarch:
           $CI_APP_REPO:$CI_APP_TAG-linux-arm64v8
       fi
       docker manifest push $CI_APP_REPO:$CI_APP_TAG
-    - |
-      docker manifest create $CI_APP_REPO:$VERSION_MAJ_MIN $CI_MANIFEST_LIST
-      if echo $OS_ARCH | grep "linux-arm64v8" >/dev/null ; then
-        docker manifest annotate --variant v8 $CI_APP_REPO:$VERSION_MAJ_MIN \
-          $CI_APP_REPO:$CI_APP_TAG-linux-arm64v8
-      fi
-      docker manifest push $CI_APP_REPO:$VERSION_MAJ_MIN
   rules:
     - if: $UPDATE_STACK == "true" && $CI_COMMIT_BRANCH == "freeze-version"
diff --git a/VERSION_MATRIX.md b/VERSION_MATRIX.md
index 3848520d1c9e5fe65f5aa8c24142817f4af4b5e0..57a108c09c46dd119b1bc18e123f7b7fdef80ad2 100644
--- a/VERSION_MATRIX.md
+++ b/VERSION_MATRIX.md
@@ -6,14 +6,14 @@ Topmost entry = Tag `latest`
 |:-----------|:---------|:-----------|:---- |:-------------|
 | 9.6.2      | 3.10.1.0 | 2.11.1[^1] | 14   | Alpine 3.18  |
 | 9.6.1      | 3.10.1.0 | n/a        | 14   | Alpine 3.18  |
-| 9.4.7      | 3.8.1.0  | 2.11.1[^1] | 14   | Alpine 3.18  |
+| 9.4.7      | 3.8.1.0  | 2.13.1     | 14   | Alpine 3.18  |
 | 9.4.6      | 3.8.1.0  | 2.11.1[^1] | 14   | Alpine 3.18  |
 | 9.4.5      | 3.8.1.0  | n/a        | 14   | Alpine 3.17  |
 | 9.4.4      | 3.8.1.0  | n/a        | 14   | Alpine 3.17  |
 | 9.4.3      | 3.8.1.0  | n/a        | 12   | Alpine 3.16  |
 | 9.4.2      | 3.8.1.0  | n/a        | 12   | Alpine 3.16  |
 | 9.4.1      | 3.8.1.0  | n/a        | 12   | Alpine 3.16  |
-| 9.2.8      | 3.6.2.0  | 2.11.1[^1] | 12   | Alpine 3.16  |
+| 9.2.8      | 3.6.2.0  | 2.13.1     | 12   | Alpine 3.16  |
 | 9.2.7[^2]  | 3.6.2.0  | n/a        | 12   | Alpine 3.16  |
 | 9.2.6[^2]  | 3.6.2.0  | n/a        | 12   | Alpine 3.16  |
 | 9.2.5[^2]  | 3.6.2.0  | n/a        | 12   | Alpine 3.16  |
diff --git a/prior/9.2.8.Dockerfile b/prior/9.2.8.Dockerfile
index 5a138426f582f763423b0153b185c4f65ea15b43..e40fc49c4880a46df947b74c115fe1cb69214b52 100644
--- a/prior/9.2.8.Dockerfile
+++ b/prior/9.2.8.Dockerfile
@@ -1,6 +1,6 @@
 ARG GHC_VERSION_BUILD=9.2.8
 ARG CABAL_VERSION_BUILD=3.6.2.0
-ARG STACK_VERSION=2.11.1
+ARG STACK_VERSION=2.13.1
 
 FROM glcr.b-data.ch/ghc/ghc-musl:9.0.2 as bootstrap
 
@@ -71,9 +71,11 @@ LABEL org.opencontainers.image.licenses="MIT" \
 
 ARG GHC_VERSION_BUILD
 ARG CABAL_VERSION_BUILD
+ARG STACK_VERSION
 
 ENV GHC_VERSION=${GHC_VERSION_BUILD} \
-    CABAL_VERSION=${CABAL_VERSION_BUILD}
+    CABAL_VERSION=${CABAL_VERSION_BUILD} \
+    STACK_VERSION=${STACK_VERSION}
 
 RUN apk add --no-cache \
     bash \
@@ -100,6 +102,7 @@ RUN apk add --no-cache \
     pcre2 \
     pcre2-dev \
     perl \
+    ## Install shadow for `stack --docker`
     shadow \
     wget \
     xz \
@@ -109,16 +112,27 @@ RUN apk add --no-cache \
     zlib-static
 
 COPY --from=bootstrap /tmp/ghc-"$GHC_VERSION"/ghc-"$GHC_VERSION"-*-alpine-linux.tar.xz /tmp/
-COPY --from=bootstrap /root/.cabal/bin/cabal /usr/bin/cabal
 
 RUN cd /tmp \
+  ## Install GHC
   && tar -xJf ghc-"$GHC_VERSION"-*-alpine-linux.tar.xz \
   && cd "ghc-$GHC_VERSION" \
   && ./configure --disable-ld-override --prefix=/usr \
   && make install \
+  ## Install Stack
+  && cd /tmp \
+  && curl -sSLO https://github.com/commercialhaskell/stack/releases/download/v"$STACK_VERSION"/stack-"$STACK_VERSION"-linux-"$(uname -m)".tar.gz \
+  && curl -sSLO https://github.com/commercialhaskell/stack/releases/download/v"$STACK_VERSION"/stack-"$STACK_VERSION"-linux-"$(uname -m)".tar.gz.sha256 \
+  && sha256sum -cs stack-"$STACK_VERSION"-linux-"$(uname -m)".tar.gz.sha256 \
+  && tar -xzf stack-"$STACK_VERSION"-linux-"$(uname -m)".tar.gz \
+  && mv stack-"$STACK_VERSION"-linux-"$(uname -m)"/stack /usr/bin/stack \
+  ## Clean up
   && rm -rf /tmp/* \
     "/usr/share/doc/ghc-$GHC_VERSION"/*
 
+## Install Cabal
+COPY --from=bootstrap /root/.cabal/bin/cabal /usr/bin/cabal
+
 FROM builder as tester
 
 WORKDIR /usr/local/src
@@ -135,14 +149,6 @@ RUN ghc -static -optl-pthread -optl-static Main.hs \
   && cabal init -n --is-executable -p tester -l MIT \
   && cabal run
 
-FROM glcr.b-data.ch/commercialhaskell/ssi:${STACK_VERSION} as ssi
-
 FROM builder as final
 
-ARG STACK_VERSION
-
-ENV STACK_VERSION=${STACK_VERSION}
-
-COPY --from=ssi /usr/local/bin/stack /usr/bin/stack
-
 CMD ["ghci"]
diff --git a/prior/9.4.7.Dockerfile b/prior/9.4.7.Dockerfile
index da7675892a1ddbc60c8b23af413ec3ed1fd10c4b..c4dba935bf7292d6b84402e4dcb74e65c902406b 100644
--- a/prior/9.4.7.Dockerfile
+++ b/prior/9.4.7.Dockerfile
@@ -1,6 +1,6 @@
 ARG GHC_VERSION_BUILD=9.4.7
 ARG CABAL_VERSION_BUILD=3.8.1.0
-ARG STACK_VERSION=2.11.1
+ARG STACK_VERSION=2.13.1
 
 FROM glcr.b-data.ch/ghc/ghc-musl:9.2.8 as bootstrap
 
@@ -64,9 +64,11 @@ LABEL org.opencontainers.image.licenses="MIT" \
 
 ARG GHC_VERSION_BUILD
 ARG CABAL_VERSION_BUILD
+ARG STACK_VERSION
 
 ENV GHC_VERSION=${GHC_VERSION_BUILD} \
-    CABAL_VERSION=${CABAL_VERSION_BUILD}
+    CABAL_VERSION=${CABAL_VERSION_BUILD} \
+    STACK_VERSION=${STACK_VERSION}
 
 RUN apk add --no-cache \
     bash \
@@ -93,6 +95,7 @@ RUN apk add --no-cache \
     pcre2 \
     pcre2-dev \
     perl \
+    ## Install shadow for `stack --docker`
     shadow \
     wget \
     xz \
@@ -102,7 +105,6 @@ RUN apk add --no-cache \
     zlib-static
 
 COPY --from=bootstrap /tmp/ghc-"$GHC_VERSION"/_build/bindist/ghc-"$GHC_VERSION"-*-alpine-linux.tar.xz /tmp/
-COPY --from=bootstrap /root/.cabal/bin/cabal /usr/local/bin/cabal
 
 RUN cd /tmp \
   # Fix https://github.com/haskell/cabal/issues/8923
@@ -112,12 +114,24 @@ RUN cd /tmp \
     curl -sSLO http://dl-cdn.alpinelinux.org/alpine/v3.16/main/"$(uname -m)"/pkgconf-1.8.1-r0.apk; \
     apk add --no-cache pkgconf-1.8.1-r0.apk; \
   fi \
+  ## Install GHC
   && tar -xJf ghc-"$GHC_VERSION"-*-alpine-linux.tar.xz \
   && cd ghc-"$GHC_VERSION"-*-alpine-linux \
   && ./configure --disable-ld-override \
   && make install \
+  ## Install Stack
+  && cd /tmp \
+  && curl -sSLO https://github.com/commercialhaskell/stack/releases/download/v"$STACK_VERSION"/stack-"$STACK_VERSION"-linux-"$(uname -m)".tar.gz \
+  && curl -sSLO https://github.com/commercialhaskell/stack/releases/download/v"$STACK_VERSION"/stack-"$STACK_VERSION"-linux-"$(uname -m)".tar.gz.sha256 \
+  && sha256sum -cs stack-"$STACK_VERSION"-linux-"$(uname -m)".tar.gz.sha256 \
+  && tar -xzf stack-"$STACK_VERSION"-linux-"$(uname -m)".tar.gz \
+  && mv stack-"$STACK_VERSION"-linux-"$(uname -m)"/stack /usr/local/bin/stack \
+  ## Clean up
   && rm -rf /tmp/*
 
+## Install Cabal
+COPY --from=bootstrap /root/.cabal/bin/cabal /usr/local/bin/cabal
+
 FROM builder as tester
 
 WORKDIR /usr/local/src
@@ -134,14 +148,6 @@ RUN ghc -static -optl-pthread -optl-static Main.hs \
   && cabal init -n --is-executable -p tester -l MIT \
   && cabal run
 
-FROM glcr.b-data.ch/commercialhaskell/ssi:${STACK_VERSION} as ssi
-
 FROM builder as final
 
-ARG STACK_VERSION
-
-ENV STACK_VERSION=${STACK_VERSION}
-
-COPY --from=ssi /usr/local/bin/stack /usr/local/bin/stack
-
 CMD ["ghci"]
diff --git a/prior/9.6.1.Dockerfile b/prior/9.6.1.Dockerfile
index 55ad1a5685a641b5e50c888608699e869a2b2af2..6d495695283798c5f29b2c273001d5d8fad59026 100644
--- a/prior/9.6.1.Dockerfile
+++ b/prior/9.6.1.Dockerfile
@@ -120,8 +120,6 @@ FROM builder as tester
 
 WORKDIR /usr/local/src
 
-WORKDIR /usr/local/src
-
 COPY Main.hs Main.hs
 
 RUN ghc -static -optl-pthread -optl-static Main.hs \
diff --git a/prior/9.6.2.Dockerfile b/prior/9.6.2.Dockerfile
new file mode 100644
index 0000000000000000000000000000000000000000..21de1a9e5be8bb5fe4bf82827ea805d07d1170c4
--- /dev/null
+++ b/prior/9.6.2.Dockerfile
@@ -0,0 +1,152 @@
+ARG GHC_VERSION_BUILD=9.6.2
+ARG CABAL_VERSION_BUILD=3.10.1.0
+ARG STACK_VERSION=2.11.1
+
+FROM glcr.b-data.ch/ghc/ghc-musl:9.4.7 as bootstrap
+
+ARG GHC_VERSION_BUILD
+ARG CABAL_VERSION_BUILD
+
+ENV GHC_VERSION=${GHC_VERSION_BUILD} \
+    CABAL_VERSION=${CABAL_VERSION_BUILD}
+
+RUN apk upgrade --no-cache \
+  && apk add --no-cache \
+    autoconf \
+    automake \
+    binutils-gold \
+    build-base \
+    coreutils \
+    cpio \
+    curl \
+    gnupg \
+    linux-headers \
+    libffi-dev \
+    llvm14 \
+    ncurses-dev \
+    perl \
+    python3 \
+    xz \
+    zlib-dev
+
+RUN cd /tmp \
+  && curl -sSLO https://downloads.haskell.org/~ghc/"$GHC_VERSION"/ghc-"$GHC_VERSION"-src.tar.xz \
+  && curl -sSLO https://downloads.haskell.org/~ghc/"$GHC_VERSION"/ghc-"$GHC_VERSION"-src.tar.xz.sig \
+  && gpg --keyserver hkps://keyserver.ubuntu.com:443 \
+    --receive-keys FFEB7CE81E16A36B3E2DED6F2DE04D4E97DB64AD || \
+    gpg --keyserver hkp://keyserver.ubuntu.com:80 \
+    --receive-keys FFEB7CE81E16A36B3E2DED6F2DE04D4E97DB64AD \
+  && gpg --verify "ghc-$GHC_VERSION-src.tar.xz.sig" "ghc-$GHC_VERSION-src.tar.xz" \
+  && tar -xJf "ghc-$GHC_VERSION-src.tar.xz" \
+  && cd "ghc-$GHC_VERSION" \
+  && ./boot.source \
+  && ./configure --disable-ld-override LD=ld.gold \
+  # Use the LLVM backend
+  # Switch llvm-targets from unknown-linux-gnueabihf->alpine-linux
+  # so we can match the llvm vendor string alpine uses
+  && sed -i -e 's/unknown-linux-gnueabihf/alpine-linux/g' llvm-targets \
+  && sed -i -e 's/unknown-linux-gnueabi/alpine-linux/g' llvm-targets \
+  && sed -i -e 's/unknown-linux-gnu/alpine-linux/g' llvm-targets \
+  && cabal update \
+  # See https://unix.stackexchange.com/questions/519092/what-is-the-logic-of-using-nproc-1-in-make-command
+  && hadrian/build binary-dist -j"$(($(nproc)+1))" \
+    --flavour=perf+llvm+split_sections \
+    --docs=none \
+  # See https://gitlab.haskell.org/ghc/ghc/-/wikis/commentary/libraries/version-history
+  && cabal install --allow-newer --constraint 'Cabal-syntax<3.11' "cabal-install-$CABAL_VERSION"
+
+FROM alpine:3.18 as builder
+
+LABEL org.opencontainers.image.licenses="MIT" \
+      org.opencontainers.image.source="https://gitlab.b-data.ch/ghc/ghc-musl" \
+      org.opencontainers.image.vendor="Olivier Benz" \
+      org.opencontainers.image.authors="Olivier Benz <olivier.benz@b-data.ch>"
+
+ARG GHC_VERSION_BUILD
+ARG CABAL_VERSION_BUILD
+
+ENV GHC_VERSION=${GHC_VERSION_BUILD} \
+    CABAL_VERSION=${CABAL_VERSION_BUILD}
+
+RUN apk add --no-cache \
+    bash \
+    build-base \
+    bzip2 \
+    bzip2-dev \
+    bzip2-static \
+    curl \
+    curl-static \
+    dpkg \
+    fakeroot \
+    git \
+    gmp-dev \
+    libcurl \
+    libffi \
+    libffi-dev \
+    llvm14 \
+    ncurses-dev \
+    ncurses-static \
+    openssl-dev \
+    openssl-libs-static \
+    pcre \
+    pcre-dev \
+    pcre2 \
+    pcre2-dev \
+    perl \
+    ## Install shadow for `stack --docker`
+    shadow \
+    wget \
+    xz \
+    xz-dev \
+    zlib \
+    zlib-dev \
+    zlib-static
+
+COPY --from=bootstrap /tmp/ghc-"$GHC_VERSION"/_build/bindist/ghc-"$GHC_VERSION"-*-alpine-linux.tar.xz /tmp/
+
+RUN cd /tmp \
+  # Fix https://github.com/haskell/cabal/issues/8923
+  && PKG_CONFIG_VERSION="$(pkg-config --version)" \
+  && if [ "${PKG_CONFIG_VERSION%.*}" = "1.9" ]; then \
+    # Downgrade pkgconf from 1.9.x to 1.8.1
+    curl -sSLO http://dl-cdn.alpinelinux.org/alpine/v3.16/main/"$(uname -m)"/pkgconf-1.8.1-r0.apk; \
+    apk add --no-cache pkgconf-1.8.1-r0.apk; \
+  fi \
+  ## Install GHC
+  && tar -xJf ghc-"$GHC_VERSION"-*-alpine-linux.tar.xz \
+  && cd ghc-"$GHC_VERSION"-*-alpine-linux \
+  && ./configure --disable-ld-override \
+  && make install \
+  ## Clean up
+  && rm -rf /tmp/*
+
+## Install Cabal
+COPY --from=bootstrap /root/.cabal/bin/cabal /usr/local/bin/cabal
+
+FROM builder as tester
+
+WORKDIR /usr/local/src
+
+COPY Main.hs Main.hs
+
+RUN ghc -static -optl-pthread -optl-static Main.hs \
+  && file Main \
+  && ./Main \
+  # Test cabal workflow
+  && mkdir cabal-test \
+  && cd cabal-test \
+  && cabal update \
+  && cabal init -n --is-executable -p tester -l MIT \
+  && cabal run
+
+FROM glcr.b-data.ch/commercialhaskell/ssi:${STACK_VERSION} as ssi
+
+FROM builder as final
+
+ARG STACK_VERSION
+
+ENV STACK_VERSION=${STACK_VERSION}
+
+COPY --from=ssi /usr/local/bin/stack /usr/local/bin/stack
+
+CMD ["ghci"]