diff --git a/modules/search_api_db/tests/src/Kernel/BackendTest.php b/modules/search_api_db/tests/src/Kernel/BackendTest.php
index 1fad980..9309044 100644
--- a/modules/search_api_db/tests/src/Kernel/BackendTest.php
+++ b/modules/search_api_db/tests/src/Kernel/BackendTest.php
@@ -122,7 +122,7 @@ class BackendTest extends BackendTestBase {
    */
   protected function updateIndex() {
     /** @var \Drupal\search_api\IndexInterface $index */
-    $index = $this->getIndex();
+    $index = $this->getEditableIndex();
 
     // Remove a field from the index and check if the change is matched in the
     // server configuration.
@@ -197,7 +197,7 @@ class BackendTest extends BackendTestBase {
    *   (optional) Whether partial matching should be enabled or disabled.
    */
   protected function editServerPartial($enable = TRUE) {
-    $server = $this->getServer();
+    $server = $this->getEditableServer();
     $backend_config = $server->getBackendConfig();
     $backend_config['partial_matches'] = $enable;
     $server->setBackendConfig($backend_config);
@@ -260,7 +260,7 @@ class BackendTest extends BackendTestBase {
    * Edits the server to change the "Minimum word length" setting.
    */
   protected function editServerMinChars() {
-    $server = $this->getServer();
+    $server = $this->getEditableServer();
     $backend_config = $server->getBackendConfig();
     $backend_config['min_chars'] = 4;
     $backend_config['partial_matches'] = FALSE;
@@ -386,13 +386,13 @@ class BackendTest extends BackendTestBase {
    * Checks that field ID changes are treated correctly (without re-indexing).
    */
   protected function checkFieldIdChanges() {
-    $this->getIndex()
+    $this->getEditableIndex()
       ->renameField('type', 'foobar')
       ->save();
 
     $results = $this->buildSearch(NULL, array('foobar,item'))->execute();
     $this->assertResults(array(1, 2, 3), $results, 'Search after renaming a field.');
-    $this->getIndex()->renameField('foobar', 'type')->save();
+    $this->getEditableIndex()->renameField('foobar', 'type')->save();
   }
 
   /**
@@ -489,8 +489,8 @@ class BackendTest extends BackendTestBase {
 
     // See whether clearing the server works.
     // Regression test for #2156151.
-    $server = $this->getServer();
-    $index = $this->getIndex();
+    $server = $this->getEditableServer();
+    $index = $this->getEditableIndex();
     $server->deleteAllIndexItems($index);
     $query = $this->buildSearch();
     $results = $query->execute();
diff --git a/src/Entity/Index.php b/src/Entity/Index.php
index d4ff98a..04e5b1d 100644
--- a/src/Entity/Index.php
+++ b/src/Entity/Index.php
@@ -16,6 +16,7 @@ use Drupal\search_api\Query\QueryInterface;
 use Drupal\search_api\Query\ResultSetInterface;
 use Drupal\search_api\SearchApiException;
 use Drupal\search_api\ServerInterface;
+use Drupal\search_api\TemporaryConfOverrideLayer\OverrideSupportingConfigEntityBase;
 use Drupal\search_api\Tracker\TrackerInterface;
 use Drupal\search_api\Utility\Utility;
 use Drupal\user\TempStoreException;
@@ -35,7 +36,7 @@ use Symfony\Component\DependencyInjection\Exception\ServiceNotFoundException;
  *     plural = "@count search indexes",
  *   ),
  *   handlers = {
- *     "storage" = "Drupal\Core\Config\Entity\ConfigEntityStorage",
+ *     "storage" = "Drupal\search_api\SearchApiConfigEntityStorage",
  *     "list_builder" = "Drupal\search_api\IndexListBuilder",
  *     "form" = {
  *       "default" = "Drupal\search_api\Form\IndexForm",
@@ -85,7 +86,7 @@ use Symfony\Component\DependencyInjection\Exception\ServiceNotFoundException;
  *   }
  * )
  */
-class Index extends ConfigEntityBase implements IndexInterface {
+class Index extends OverrideSupportingConfigEntityBase implements IndexInterface {
 
   /**
    * The ID of the index.
@@ -1258,42 +1259,46 @@ class Index extends ConfigEntityBase implements IndexInterface {
     try {
       // Fake an original for inserts to make code cleaner.
       /** @var \Drupal\search_api\IndexInterface $original */
-      $original = $update ? $this->original : static::create(array('status' => FALSE));
+      $original = $update ? $this->overrideOriginal : static::create(array('status' => FALSE));
       $index_task_manager = \Drupal::getContainer()
         ->get('search_api.index_task_manager');
 
-      if ($this->status() && $original->status()) {
+      // load the possibly overridden values to compare of "effective" changes
+      /** @var \Drupal\search_api\Entity\Index $me */
+      $me = &$this->myOverride;
+
+      if ($me->status() && $original->status()) {
         // React on possible changes that would require re-indexing, etc.
-        $this->reactToServerSwitch($original);
-        $this->reactToDatasourceSwitch($original);
-        $this->reactToTrackerSwitch($original);
-        $this->reactToProcessorChanges($original);
+        $me->reactToServerSwitch($original);
+        $me->reactToDatasourceSwitch($original);
+        $me->reactToTrackerSwitch($original);
+        $me->reactToProcessorChanges($original);
       }
-      elseif (!$this->status() && $original->status()) {
-        if ($this->hasValidTracker()) {
+      elseif (!$me->status() && $original->status()) {
+        if ($me->hasValidTracker()) {
           $index_task_manager->stopTracking($original);
         }
         if ($original->isServerEnabled()) {
           $original->getServerInstance()->removeIndex($original);
         }
       }
-      elseif ($this->status() && !$original->status()) {
-        $this->getServerInstance()->addIndex($this);
-        if ($this->hasValidTracker()) {
-          $index_task_manager->startTracking($this);
+      elseif ($me->status() && !$original->status()) {
+        $me->getServerInstance()->addIndex($me);
+        if ($me->hasValidTracker()) {
+          $index_task_manager->startTracking($me);
         }
       }
 
-      if (!$index_task_manager->isTrackingComplete($this)) {
+      if (!$index_task_manager->isTrackingComplete($me)) {
         // Give tests and site admins the possibility to disable the use of a
         // batch for tracking items. Also, do not use a batch if running in the
         // CLI.
         $use_batch = \Drupal::state()->get('search_api_use_tracking_batch', TRUE);
         if (!$use_batch || php_sapi_name() == 'cli') {
-          $index_task_manager->addItemsAll($this);
+          $index_task_manager->addItemsAll($me);
         }
         else {
-          $index_task_manager->addItemsBatch($this);
+          $index_task_manager->addItemsBatch($me);
         }
       }
 
@@ -1304,7 +1309,7 @@ class Index extends ConfigEntityBase implements IndexInterface {
         \Drupal::cache('discovery')->delete('views:wizard');
       }
 
-      Cache::invalidateTags($this->getCacheTags());
+      Cache::invalidateTags($me->getCacheTags());
     }
     catch (SearchApiException $e) {
       watchdog_exception('search_api', $e);
diff --git a/src/Entity/Server.php b/src/Entity/Server.php
index 157bff0..b315a57 100644
--- a/src/Entity/Server.php
+++ b/src/Entity/Server.php
@@ -2,12 +2,12 @@
 
 namespace Drupal\search_api\Entity;
 
-use Drupal\Core\Config\Entity\ConfigEntityBase;
 use Drupal\Core\Entity\EntityStorageInterface;
 use Drupal\search_api\IndexInterface;
 use Drupal\search_api\Query\QueryInterface;
 use Drupal\search_api\SearchApiException;
 use Drupal\search_api\ServerInterface;
+use Drupal\search_api\TemporaryConfOverrideLayer\OverrideSupportingConfigEntityBase;
 
 /**
  * Defines the search server configuration entity.
@@ -22,7 +22,7 @@ use Drupal\search_api\ServerInterface;
  *     plural = "@count search servers",
  *   ),
  *   handlers = {
- *     "storage" = "Drupal\Core\Config\Entity\ConfigEntityStorage",
+ *     "storage" = "Drupal\search_api\SearchApiConfigEntityStorage",
  *     "form" = {
  *       "default" = "Drupal\search_api\Form\ServerForm",
  *       "edit" = "Drupal\search_api\Form\ServerForm",
@@ -56,7 +56,7 @@ use Drupal\search_api\ServerInterface;
  *   }
  * )
  */
-class Server extends ConfigEntityBase implements ServerInterface {
+class Server extends OverrideSupportingConfigEntityBase implements ServerInterface {
 
   /**
    * The ID of the server.
@@ -476,17 +476,21 @@ class Server extends ConfigEntityBase implements ServerInterface {
    * {@inheritdoc}
    */
   public function postSave(EntityStorageInterface $storage, $update = TRUE) {
-    if ($this->hasValidBackend()) {
+    // load the possibly overridden values to compare of "effective" changes
+    /** @var \Drupal\search_api\Entity\Index $me */
+    $me = &$this->myOverride;
+
+    if ($me->hasValidBackend()) {
       if ($update) {
-        $reindexing_necessary = $this->getBackend()->postUpdate();
+        $reindexing_necessary = $me->getBackend()->postUpdate();
         if ($reindexing_necessary) {
-          foreach ($this->getIndexes() as $index) {
+          foreach ($me->getIndexes() as $index) {
             $index->reindex();
           }
         }
       }
       else {
-        $this->getBackend()->postInsert();
+        $me->getBackend()->postInsert();
       }
     }
   }
diff --git a/src/IndexInterface.php b/src/IndexInterface.php
index 32e02fc..4334c28 100644
--- a/src/IndexInterface.php
+++ b/src/IndexInterface.php
@@ -2,18 +2,18 @@
 
 namespace Drupal\search_api;
 
-use Drupal\Core\Config\Entity\ConfigEntityInterface;
 use Drupal\search_api\Datasource\DatasourceInterface;
 use Drupal\search_api\Item\FieldInterface;
 use Drupal\search_api\Processor\ProcessorInterface;
 use Drupal\search_api\Query\QueryInterface;
 use Drupal\search_api\Query\ResultSetInterface;
+use Drupal\search_api\TemporaryConfOverrideLayer\OverrideSupportingConfigEntityInterface;
 use Drupal\search_api\Tracker\TrackerInterface;
 
 /**
  * Defines the interface for index entities.
  */
-interface IndexInterface extends ConfigEntityInterface {
+interface IndexInterface extends OverrideSupportingConfigEntityInterface {
 
   /**
    * String used to separate a datasource prefix from the rest of an identifier.
diff --git a/src/SearchApiConfigEntityStorage.php b/src/SearchApiConfigEntityStorage.php
new file mode 100644
index 0000000..b11759f
--- /dev/null
+++ b/src/SearchApiConfigEntityStorage.php
@@ -0,0 +1,65 @@
+<?php
+/**
+ * @file
+ * Contains Drupal\search_api\SearchApiConfigEntityStorage.
+ */
+namespace Drupal\search_api;
+
+use Drupal\Core\Entity\EntityInterface;
+use Drupal\search_api\TemporaryConfOverrideLayer\OverrideSupportingConfigEntityStorage;
+
+/**
+ * Class SearchApiConfigEntityStorage
+ *
+ * This class sets an overrideOriginal key on config entities being saved to
+ * to easily track effective changes.
+ *
+ * @package Drupal\search_api
+ */
+class SearchApiConfigEntityStorage extends OverrideSupportingConfigEntityStorage {
+  protected function doPreSave(EntityInterface $entity) {
+    $id = $entity->id();
+
+    // Track the original ID.
+    if ($entity->getOriginalId() !== NULL) {
+      $id = $entity->getOriginalId();
+    }
+
+    // Track if this entity exists already.
+    $id_exists = $this->has($id, $entity);
+
+    // Load the original entities, if any.
+    if ($id_exists) {
+      // this will load overrides if they exist
+      $override_original = $this->loadUnchanged($id);
+      // this will load the entity without overrides
+      $original = $this->loadOverrideFree($id);
+
+      if (!isset($entity->original)) {
+        $entity->original = $original;
+      }
+      if (!isset($entity->overrideOriginal)) {
+        $entity->overrideOriginal = $override_original;
+      }
+    }
+
+    return parent::doPreSave($entity);
+  }
+
+  protected function doPostSave(EntityInterface $entity, $update) {
+    // load the override to be able to compare for "effective" changes
+    if (!isset($entity->myOverride)) {
+      $id = $entity->id();
+      $this->resetCache([$id]);
+      $my_override_version = $this->load($id);
+      $entity->myOverride = $my_override_version;
+    }
+
+    parent::doPostSave($entity, $update);
+
+    // remove the manually added fields, just like the parent does for original
+    unset($entity->overrideOriginal);
+    unset($entity->myOverride);
+  }
+
+}
\ No newline at end of file
diff --git a/src/ServerInterface.php b/src/ServerInterface.php
index 537c175..f8e83a9 100644
--- a/src/ServerInterface.php
+++ b/src/ServerInterface.php
@@ -2,13 +2,13 @@
 
 namespace Drupal\search_api;
 
-use Drupal\Core\Config\Entity\ConfigEntityInterface;
 use Drupal\search_api\Backend\BackendSpecificInterface;
+use Drupal\search_api\TemporaryConfOverrideLayer\OverrideSupportingConfigEntityInterface;
 
 /**
  * Defines the interface for server entities.
  */
-interface ServerInterface extends ConfigEntityInterface, BackendSpecificInterface {
+interface ServerInterface extends OverrideSupportingConfigEntityInterface, BackendSpecificInterface {
 
   /**
    * Determines whether this server supports a given feature.
diff --git a/src/TemporaryConfOverrideLayer/OverrideSupportingConfigEntityBase.php b/src/TemporaryConfOverrideLayer/OverrideSupportingConfigEntityBase.php
new file mode 100644
index 0000000..2140a3d
--- /dev/null
+++ b/src/TemporaryConfOverrideLayer/OverrideSupportingConfigEntityBase.php
@@ -0,0 +1,57 @@
+<?php
+
+namespace Drupal\search_api\TemporaryConfOverrideLayer;
+
+use Drupal\Core\Config\Entity\ConfigEntityBase;
+use Drupal\Core\Config\ImmutableConfigException;
+
+/**
+ * Defines a base configuration entity class for config entities supporting
+ * config overrides.
+ *
+ * @todo: Remove once a solution is found in: https://www.drupal.org/node/2744057
+ * @ingroup entity_api
+ */
+abstract class OverrideSupportingConfigEntityBase extends ConfigEntityBase implements OverrideSupportingConfigEntityInterface {
+
+  /**
+   * The entity may contain overridden values and may therefore not be saved.
+   *
+   * @var bool
+   */
+  protected $isImmutable = FALSE;
+
+  /**
+   * {@inheritdoc}
+   */
+  public function isImmutable() {
+    return $this->isImmutable;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getEditableEntity() {
+    if ($this->isNew() || !$this->isImmutable()) {
+      return $this;
+    }
+
+    /** @var \Drupal\Core\Config\Entity\ConfigEntityStorageInterface $storage */
+    $storage = $this->entityTypeManager()->getStorage($this->entityTypeId);
+    return $storage->loadOverrideFree($this->id());
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function save() {
+    // Ensure an immutable configuration entity is not saved if the entity type
+    // supports config overrides.
+    if ($this->isImmutable()) {
+      throw new ImmutableConfigException("Can not save immutable configuration entity {$this->id()}. Load the entity via \\Drupal\\Core\\Config\\Entity\\ConfigEntityStorageInterface::loadOverrideFree() / ::loadMultipleOverrideFree() to retrieve a mutable configuration entity object");
+    }
+
+    return parent::save();
+  }
+
+}
diff --git a/src/TemporaryConfOverrideLayer/OverrideSupportingConfigEntityInterface.php b/src/TemporaryConfOverrideLayer/OverrideSupportingConfigEntityInterface.php
new file mode 100644
index 0000000..4412a46
--- /dev/null
+++ b/src/TemporaryConfOverrideLayer/OverrideSupportingConfigEntityInterface.php
@@ -0,0 +1,35 @@
+<?php
+
+namespace Drupal\search_api\TemporaryConfOverrideLayer;
+
+use \Drupal\Core\Config\Entity\ConfigEntityInterface;
+
+/**
+ * Defines a common interface for configuration entities supporting
+ * config overrides.
+ *
+ * @todo: Remove once a solution is found in: https://www.drupal.org/node/2744057
+ * @ingroup config_api
+ * @ingroup entity_api
+ */
+interface OverrideSupportingConfigEntityInterface extends ConfigEntityInterface {
+
+  /**
+   * Returns whether the entity might contain overridden values and may
+   * therefore not be saved.
+   * 
+   * @return bool
+   *   TRUE if the entity is immutable, FALSE otherwise.
+   */
+  public function isImmutable();
+
+  /**
+   * Returns an editable version of this config entity.
+   *
+   * May return the same instance if this instance is not immutable.
+   *
+   * @return \Drupal\search_api\TemporaryConfOverrideLayer\OverrideSupportingConfigEntityInterface
+   */
+  public function getEditableEntity();
+
+}
diff --git a/src/TemporaryConfOverrideLayer/OverrideSupportingConfigEntityStorage.php b/src/TemporaryConfOverrideLayer/OverrideSupportingConfigEntityStorage.php
new file mode 100644
index 0000000..8c7bb4b
--- /dev/null
+++ b/src/TemporaryConfOverrideLayer/OverrideSupportingConfigEntityStorage.php
@@ -0,0 +1,111 @@
+<?php
+
+namespace Drupal\search_api\TemporaryConfOverrideLayer;
+
+use Drupal\Core\Cache\CacheableMetadata;
+use Drupal\Core\Config\Entity\ConfigEntityStorage;
+use Drupal\Core\Config\Entity\ConfigEntityStorageInterface;
+use Drupal\Core\Config\ImmutableConfigException;
+use Drupal\Core\Entity\EntityInterface;
+use Drupal\Core\Entity\EntityMalformedException;
+
+/**
+ * Defines the storage class for configuration entities supporting
+ * config overrides.
+ *
+ * Configuration object names of configuration entities are comprised of two
+ * parts, separated by a dot:
+ * - config_prefix: A string denoting the owner (module/extension) of the
+ *   configuration object, followed by arbitrary other namespace identifiers
+ *   that are declared by the owning extension; e.g., 'node.type'. The
+ *   config_prefix does NOT contain a trailing dot. It is defined by the entity
+ *   type's annotation.
+ * - ID: A string denoting the entity ID within the entity type namespace; e.g.,
+ *   'article'. Entity IDs may contain dots/periods. The entire remaining string
+ *   after the config_prefix in a config name forms the entity ID. Additional or
+ *   custom suffixes are not possible.
+ *
+ * @todo: Remove once a solution is found in: https://www.drupal.org/node/2744057
+ * @ingroup entity_api
+ */
+class OverrideSupportingConfigEntityStorage extends ConfigEntityStorage implements ConfigEntityStorageInterface {
+
+  /**
+   * {@inheritdoc}
+   */
+  protected function doLoadMultiple(array $ids = NULL) {
+    $prefix = $this->getPrefix();
+
+    // Get the names of the configuration entities we are going to load.
+    if ($ids === NULL) {
+      $names = $this->configFactory->listAll($prefix);
+    }
+    else {
+      $names = array();
+      foreach ($ids as $id) {
+        // Add the prefix to the ID to serve as the configuration object name.
+        $names[] = $prefix . $id;
+      }
+    }
+
+    // Load all of the configuration entities.
+    /** @var \Drupal\Core\Config\Config[] $configs */
+    $configs = [];
+    $records = [];
+    foreach ($this->configFactory->loadMultiple($names) as $config) {
+      $id = $config->get($this->idKey);
+      if ($this->overrideFree) {
+        $config_record = $config->getOriginal(NULL, FALSE);
+        $config_record['isImmutable'] = FALSE;
+      } else {
+        $config_record = $config->get();
+        $config_record['isImmutable'] = TRUE;
+      }
+      $records[$id] = $config_record;
+      $configs[$id] = $config;
+    }
+    $entities = $this->mapFromStorageRecords($records, $configs);
+
+    // Config entities wrap config objects, and therefore they need to inherit
+    // the cacheability metadata of config objects (to ensure e.g. additional
+    // cacheability metadata added by config overrides is not lost).
+    foreach ($entities as $id => $entity) {
+      // But rather than simply inheriting all cacheability metadata of config
+      // objects, we need to make sure the self-referring cache tag that is
+      // present on Config objects is not added to the Config entity. It must be
+      // removed for 3 reasons:
+      // 1. When renaming/duplicating a Config entity, the cache tag of the
+      //    original config object would remain present, which would be wrong.
+      // 2. Some Config entities choose to not use the cache tag that the under-
+      //    lying Config object provides by default (For performance and
+      //    cacheability reasons it may not make sense to have a unique cache
+      //    tag for every Config entity. The DateFormat Config entity specifies
+      //    the 'rendered' cache tag for example, because A) date formats are
+      //    changed extremely rarely, so invalidating all render cache items is
+      //    fine, B) it means fewer cache tags per page.).
+      // 3. Fewer cache tags is better for performance.
+      $self_referring_cache_tag = ['config:' . $configs[$id]->getName()];
+      $config_cacheability = CacheableMetadata::createFromObject($configs[$id]);
+      $config_cacheability->setCacheTags(array_diff($config_cacheability->getCacheTags(), $self_referring_cache_tag));
+      $entity->addCacheableDependency($config_cacheability);
+    }
+
+    return $entities;
+  }
+
+  /**
+   * Implements Drupal\Core\Entity\EntityStorageInterface::save().
+   *
+   * @throws EntityMalformedException
+   *   When attempting to save a configuration entity that has no ID.
+   */
+  public function save(EntityInterface $entity) {
+    // Ensure an immutable configuration entity is not saved.
+    if ($entity instanceof OverrideSupportingConfigEntityInterface && $entity->isImmutable()) {
+      throw new ImmutableConfigException("Can not save immutable configuration entity {$entity->id()}. Load the entity via \\Drupal\\Core\\Config\\Entity\\ConfigEntityStorageInterface::loadOverrideFree() / ::loadMultipleOverrideFree() to retrieve a mutable configuration entity object");
+    }
+
+    return parent::save($entity);
+  }
+
+}
diff --git a/src/UnsavedIndexConfiguration.php b/src/UnsavedIndexConfiguration.php
index 4e2a803..37adbda 100644
--- a/src/UnsavedIndexConfiguration.php
+++ b/src/UnsavedIndexConfiguration.php
@@ -1064,4 +1064,17 @@ class UnsavedIndexConfiguration implements IndexInterface, UnsavedConfigurationI
     return $this->entity->getThirdPartyProviders();
   }
 
+  /**
+   * {@inheritdoc}
+   */
+  public function isImmutable() {
+    return FALSE;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getEditableEntity() {
+    return $this;
+  }
 }
diff --git a/tests/src/Kernel/BackendTestBase.php b/tests/src/Kernel/BackendTestBase.php
index adb91c9..ab956ba 100644
--- a/tests/src/Kernel/BackendTestBase.php
+++ b/tests/src/Kernel/BackendTestBase.php
@@ -178,7 +178,7 @@ abstract class BackendTestBase extends KernelTestBase {
    * Enables the "HTML Filter" processor for the index.
    */
   protected function enableHtmlFilter() {
-    $index = $this->getIndex();
+    $index = $this->getEditableIndex();
 
     /** @var \Drupal\search_api\Processor\ProcessorInterface $processor */
     $processor = $index->createPlugin('processor', 'html_filter');
@@ -191,7 +191,7 @@ abstract class BackendTestBase extends KernelTestBase {
    * Disables the "HTML Filter" processor for the index.
    */
   protected function disableHtmlFilter() {
-    $index = $this->getIndex();
+    $index = $this->getEditableIndex();
     $index->removeField('body');
     $index->removeProcessor('html_filter');
     $index->save();
@@ -844,7 +844,7 @@ abstract class BackendTestBase extends KernelTestBase {
    * @see https://www.drupal.org/node/1916474
    */
   protected function regressionTest1916474() {
-    $index = $this->getIndex();
+    $index = $this->getEditableIndex();
     $this->addField($index, 'prices', 'decimal');
     $success = $index->save();
     $this->assertTrue($success, 'The index field settings were successfully changed.');
@@ -900,7 +900,7 @@ abstract class BackendTestBase extends KernelTestBase {
    * @see https://www.drupal.org/node/2616268
    */
   protected function regressionTest2471509() {
-    $index = $this->getIndex();
+    $index = $this->getEditableIndex();
     $this->addField($index, 'body');
     $index->save();
     $this->indexItems($this->indexId);
@@ -913,7 +913,7 @@ abstract class BackendTestBase extends KernelTestBase {
     $count = $this->indexItems($this->indexId);
     $this->assertEquals(1, $count, 'Indexing an item with a word longer than 50 characters worked.');
 
-    $index = $this->getIndex();
+    $index = $this->getEditableIndex();
     $index->getField('body')->setType('string');
     $index->save();
     $count = $this->indexItems($this->indexId);
@@ -959,10 +959,10 @@ abstract class BackendTestBase extends KernelTestBase {
 
     // Test the same body when indexed as a string (255 characters limit should
     // not be reached).
-    $index = $this->getIndex();
+    $index = $this->getEditableIndex();
     $index->getField('body')->setType('string');
     $index->save();
-    $count = $index->indexItems();
+    $count = $this->indexItems($this->indexId);
     $this->assertEquals($entity_count, $count, 'Switching type from text to string worked.');
 
     $query = $this->buildSearch(NULL, array("body,$mb_body"));
@@ -1047,6 +1047,17 @@ abstract class BackendTestBase extends KernelTestBase {
   }
 
   /**
+   * Retrieves the search server used by this test in an editable state.
+   *
+   * @return \Drupal\search_api\ServerInterface
+   *   The search server.
+   */
+  protected function getEditableServer() {
+    return $this->getServer()
+      ->getEditableEntity();
+  }
+
+  /**
    * Retrieves the search index used by this test.
    *
    * @return \Drupal\search_api\IndexInterface
@@ -1057,6 +1068,17 @@ abstract class BackendTestBase extends KernelTestBase {
   }
 
   /**
+   * Retrieves the search index used by this test in an editable state.
+   *
+   * @return \Drupal\search_api\IndexInterface
+   *   The search index.
+   */
+  protected function getEditableIndex() {
+    return $this->getIndex()
+      ->getEditableEntity();
+  }
+
+  /**
    * Adds a field to a search index.
    *
    * The index will not be saved automatically.
