diff --git a/ci/nightly/pipeline.template.yml b/ci/nightly/pipeline.template.yml
index e8c9ab5648133..ecac885137ef6 100644
--- a/ci/nightly/pipeline.template.yml
+++ b/ci/nightly/pipeline.template.yml
@@ -888,6 +888,18 @@ steps:
               composition: platform-checks
               args: [--scenario=UpgradeEntireMz, "--seed=$BUILDKITE_JOB_ID"]
 
+      - id: checks-lts-upgrade
+        label: "Checks LTS upgrade, whole-Mz restart"
+        depends_on: build-aarch64
+        timeout_in_minutes: 180
+        parallelism: 2
+        agents:
+          queue: hetzner-aarch64-16cpu-32gb
+        plugins:
+          - ./ci/plugins/mzcompose:
+              composition: platform-checks
+              args: [--scenario=UpgradeEntireMzFromLatestLTS, "--seed=$BUILDKITE_JOB_ID"]
+
       - id: checks-preflight-check-rollback
         label: "Checks preflight-check and roll back upgrade"
         depends_on: build-aarch64
diff --git a/misc/python/materialize/checks/scenarios_upgrade.py b/misc/python/materialize/checks/scenarios_upgrade.py
index fe9a9bd26c470..2c0b0aab80a0c 100644
--- a/misc/python/materialize/checks/scenarios_upgrade.py
+++ b/misc/python/materialize/checks/scenarios_upgrade.py
@@ -23,7 +23,7 @@
 from materialize.checks.scenarios import Scenario
 from materialize.mz_version import MzVersion
 from materialize.mzcompose.services.materialized import LEADER_STATUS_HEALTHCHECK
-from materialize.version_list import get_published_minor_mz_versions
+from materialize.version_list import LTS_VERSIONS, get_published_minor_mz_versions
 
 # late initialization
 _minor_versions: list[MzVersion] | None = None
@@ -83,6 +83,40 @@ def start_mz_read_only(
     )
 
 
+class UpgradeEntireMzFromLatestLTS(Scenario):
+    """Upgrade the entire Mz instance from the last LTS version without any intermediate steps. This makes sure our LTS releases for self-managed Materialize stay upgradable."""
+
+    def base_version(self) -> MzVersion:
+        return LTS_VERSIONS[-1]
+
+    def actions(self) -> list[Action]:
+        print(f"Upgrading from tag {self.base_version()}")
+        return [
+            StartMz(
+                self,
+                tag=self.base_version(),
+            ),
+            Initialize(self),
+            Manipulate(self, phase=1),
+            KillMz(
+                capture_logs=True
+            ),  #  We always use True here otherwise docker-compose will lose the pre-upgrade logs
+            StartMz(
+                self,
+                tag=None,
+            ),
+            Manipulate(self, phase=2),
+            Validate(self),
+            # A second restart while already on the new version
+            KillMz(capture_logs=True),
+            StartMz(
+                self,
+                tag=None,
+            ),
+            Validate(self),
+        ]
+
+
 class UpgradeEntireMz(Scenario):
     """Upgrade the entire Mz instance from the last released version."""
 
diff --git a/misc/python/materialize/version_list.py b/misc/python/materialize/version_list.py
index a830c82bee282..041a2043d3821 100644
--- a/misc/python/materialize/version_list.py
+++ b/misc/python/materialize/version_list.py
@@ -27,6 +27,11 @@
 
 MZ_ROOT = Path(os.environ["MZ_ROOT"])
 
+LTS_VERSIONS = [
+    MzVersion.parse_mz("v0.130.1"),  # v25.1.0
+    # Put new versions at the bottom
+]
+
 # not released on Docker
 INVALID_VERSIONS = {
     MzVersion.parse_mz("v0.52.1"),
diff --git a/test/legacy-upgrade/mzcompose.py b/test/legacy-upgrade/mzcompose.py
index a839aabe7e23b..aea017eaccc00 100644
--- a/test/legacy-upgrade/mzcompose.py
+++ b/test/legacy-upgrade/mzcompose.py
@@ -29,6 +29,7 @@
 from materialize.mzcompose.services.testdrive import Testdrive
 from materialize.mzcompose.services.zookeeper import Zookeeper
 from materialize.version_list import (
+    LTS_VERSIONS,
     VersionsFromDocs,
     get_all_published_mz_versions,
     get_published_minor_mz_versions,
@@ -131,6 +132,15 @@ def workflow_default(c: Composition, parser: WorkflowArgumentParser) -> None:
         test_upgrade_from_version(
             c, "current_source", priors=[], filter=args.filter, zero_downtime=False
         )
+        # Direct upgrade from latest LTS version without any inbetween versions
+        test_upgrade_from_version(
+            c,
+            f"{LTS_VERSIONS[-1]}",
+            priors=[],
+            filter=args.filter,
+            zero_downtime=False,
+            lts_upgrade=True,
+        )
 
 
 def get_all_and_latest_two_minor_mz_versions(
@@ -152,6 +162,7 @@ def test_upgrade_from_version(
     priors: list[MzVersion],
     filter: str,
     zero_downtime: bool,
+    lts_upgrade: bool = False,
 ) -> None:
     print(
         f"+++ Testing {'0dt upgrade' if zero_downtime else 'regular upgrade'} from Materialize {from_version} to current_source."
@@ -241,7 +252,7 @@ def test_upgrade_from_version(
         c.kill(mz_service)
         c.rm(mz_service, "testdrive")
 
-    if from_version != "current_source":
+    if from_version != "current_source" and not lts_upgrade:
         # We can't skip in-between minor versions anymore, so go through all of them
         for version in get_published_minor_mz_versions(newest_first=False):
             if version <= from_version: