diff --git a/package_manager/src/Stage.php b/package_manager/src/Stage.php
index 9ae7ebfe4b08ce53bc33b0c649150464ce6e3605..71c4ceac261b57f039870f75e7a8e81b4236b41f 100644
--- a/package_manager/src/Stage.php
+++ b/package_manager/src/Stage.php
@@ -320,17 +320,18 @@ class Stage {
     $active_dir = $this->pathLocator->getProjectRoot();
     $stage_dir = $this->getStageDirectory();
 
-    $event = new PreApplyEvent($this);
-    $this->tempStore->set(self::TEMPSTORE_APPLY_TIME_KEY, $this->time->getRequestTime());
-    // If an error occurs while dispatching the event, ensure that ::destroy()
+    // If an error occurs while dispatching the events, ensure that ::destroy()
     // doesn't think we're in the middle of applying the staged changes to the
     // active directory.
-    $this->dispatch($event, function (): void {
+    $release_apply = function (): void {
       $this->tempStore->delete(self::TEMPSTORE_APPLY_TIME_KEY);
-    });
+    };
+
+    $event = new PreApplyEvent($this);
+    $this->tempStore->set(self::TEMPSTORE_APPLY_TIME_KEY, $this->time->getRequestTime());
+    $this->dispatch($event, $release_apply);
 
     $this->committer->commit($stage_dir, $active_dir, $event->getExcludedPaths());
-    $this->tempStore->delete(self::TEMPSTORE_APPLY_TIME_KEY);
 
     // Rebuild the container and clear all caches, to ensure that new services
     // are picked up.
@@ -340,7 +341,8 @@ class Stage {
     // unlikely to call newly added code during the current request.
     $this->eventDispatcher = \Drupal::service('event_dispatcher');
 
-    $this->dispatch(new PostApplyEvent($this));
+    $this->dispatch(new PostApplyEvent($this), $release_apply);
+    $release_apply();
   }
 
   /**
diff --git a/package_manager/tests/src/Kernel/StageTest.php b/package_manager/tests/src/Kernel/StageTest.php
index b08b47d8c7422c945292a4207b258500a4bdfd42..90ac26e4d9f26251fbce4b5ae45a4f3642049370 100644
--- a/package_manager/tests/src/Kernel/StageTest.php
+++ b/package_manager/tests/src/Kernel/StageTest.php
@@ -4,7 +4,9 @@ namespace Drupal\Tests\package_manager\Kernel;
 
 use Drupal\Component\Datetime\Time;
 use Drupal\Core\DependencyInjection\ContainerBuilder;
+use Drupal\package_manager\Event\PostApplyEvent;
 use Drupal\package_manager\Event\PreApplyEvent;
+use Drupal\package_manager\Event\StageEvent;
 use Drupal\package_manager\Exception\StageException;
 
 /**
@@ -14,6 +16,24 @@ use Drupal\package_manager\Exception\StageException;
  */
 class StageTest extends PackageManagerKernelTestBase {
 
+  /**
+   * {@inheritdoc}
+   */
+  protected static $modules = ['system'];
+
+  /**
+   * {@inheritdoc}
+   */
+  protected function setUp(): void {
+    parent::setUp();
+
+    $this->installConfig('system');
+    $this->config('system.site')->set('uuid', $this->randomMachineName())->save();
+    // Ensure that the core update system thinks that System's post-update
+    // functions have run.
+    $this->registerPostUpdateFunctions();
+  }
+
   /**
    * {@inheritdoc}
    */
@@ -22,6 +42,11 @@ class StageTest extends PackageManagerKernelTestBase {
 
     $container->getDefinition('datetime.time')
       ->setClass(TestTime::class);
+
+    // Since this test adds arbitrary event listeners that aren't services, we
+    // need to ensure they will persist even if the container is rebuilt when
+    // staged changes are applied.
+    $container->getDefinition('event_dispatcher')->addTag('persist');
   }
 
   /**
@@ -29,12 +54,7 @@ class StageTest extends PackageManagerKernelTestBase {
    * @covers ::getStagingRoot
    */
   public function testGetStageDirectory(): void {
-    $this->container->get('module_installer')->install(['system']);
-    // Ensure we have an up-to-date-container.
-    $this->container = $this->container->get('kernel')->getContainer();
-
-    // Ensure that a site ID was generated.
-    // @see system_install()
+    // Ensure that a site ID was generated in ::setUp().
     $site_id = $this->config('system.site')->get('uuid');
     $this->assertNotEmpty($site_id);
 
@@ -68,16 +88,62 @@ class StageTest extends PackageManagerKernelTestBase {
    */
   public function providerDestroyDuringApply(): array {
     return [
-      'force destroy, not stale' => [TRUE, 1, TRUE],
-      'regular destroy, not stale' => [FALSE, 1, TRUE],
-      'force destroy, stale' => [TRUE, 7200, FALSE],
-      'regular destroy, stale' => [FALSE, 7200, FALSE],
+      'force destroy on pre-apply, fresh' => [
+        PreApplyEvent::class,
+        TRUE,
+        1,
+        TRUE,
+      ],
+      'destroy on pre-apply, fresh' => [
+        PreApplyEvent::class,
+        FALSE,
+        1,
+        TRUE,
+      ],
+      'force destroy on pre-apply, stale' => [
+        PreApplyEvent::class,
+        TRUE,
+        7200,
+        FALSE,
+      ],
+      'destroy on pre-apply, stale' => [
+        PreApplyEvent::class,
+        FALSE,
+        7200,
+        FALSE,
+      ],
+      'force destroy on post-apply, fresh' => [
+        PostApplyEvent::class,
+        TRUE,
+        1,
+        TRUE,
+      ],
+      'destroy on post-apply, fresh' => [
+        PostApplyEvent::class,
+        FALSE,
+        1,
+        TRUE,
+      ],
+      'force destroy on post-apply, stale' => [
+        PostApplyEvent::class,
+        TRUE,
+        7200,
+        FALSE,
+      ],
+      'destroy on post-apply, stale' => [
+        PostApplyEvent::class,
+        FALSE,
+        7200,
+        FALSE,
+      ],
     ];
   }
 
   /**
    * Tests destroying a stage while applying it.
    *
+   * @param string $event_class
+   *   The event class for which to attempt to destroy the stage.
    * @param bool $force
    *   Whether or not the stage should be force destroyed.
    * @param int $time_offset
@@ -88,8 +154,8 @@ class StageTest extends PackageManagerKernelTestBase {
    *
    * @dataProvider providerDestroyDuringApply
    */
-  public function testDestroyDuringApply(bool $force, int $time_offset, bool $expect_exception): void {
-    $listener = function (PreApplyEvent $event) use ($force, $time_offset): void {
+  public function testDestroyDuringApply(string $event_class, bool $force, int $time_offset, bool $expect_exception): void {
+    $listener = function (StageEvent $event) use ($force, $time_offset): void {
       // Simulate that a certain amount of time has passed since we started
       // applying staged changes. After a point, it should be possible to
       // destroy the stage even if it hasn't finished.
@@ -102,7 +168,7 @@ class StageTest extends PackageManagerKernelTestBase {
       $event->getStage()->destroy($force);
     };
     $this->container->get('event_dispatcher')
-      ->addListener(PreApplyEvent::class, $listener);
+      ->addListener($event_class, $listener);
 
     $stage = $this->createStage();
     $stage->create();
diff --git a/tests/src/Kernel/AutomaticUpdatesKernelTestBase.php b/tests/src/Kernel/AutomaticUpdatesKernelTestBase.php
index 277027b7cb81b5aaf26106c7ba012d95d3046052..be9866e59914e882f963b9462a761f5ab7b9644f 100644
--- a/tests/src/Kernel/AutomaticUpdatesKernelTestBase.php
+++ b/tests/src/Kernel/AutomaticUpdatesKernelTestBase.php
@@ -55,15 +55,8 @@ abstract class AutomaticUpdatesKernelTestBase extends PackageManagerKernelTestBa
     $this->installConfig('update');
 
     // Make the update system think that all of System's post-update functions
-    // have run. Since kernel tests don't normally install modules and register
-    // their updates, we need to do this so that all validators are tested from
-    // a clean, fully up-to-date state.
-    $updates = $this->container->get('update.post_update_registry')
-      ->getPendingUpdateFunctions();
-
-    $this->container->get('keyvalue')
-      ->get('post_update')
-      ->set('existing_updates', $updates);
+    // have run.
+    $this->registerPostUpdateFunctions();
 
     // By default, pretend we're running Drupal core 9.8.0 and a non-security
     // update to 9.8.1 is available.