diff --git a/core/lib/Drupal/Core/Config/DatabaseStorage.php b/core/lib/Drupal/Core/Config/DatabaseStorage.php
index bca57388..0ed62da3 100644
--- a/core/lib/Drupal/Core/Config/DatabaseStorage.php
+++ b/core/lib/Drupal/Core/Config/DatabaseStorage.php
@@ -84,7 +84,10 @@ class DatabaseStorage implements StorageInterface {
   public function read($name) {
     $data = FALSE;
     try {
-      $raw = $this->connection->query('SELECT [data] FROM {' . $this->connection->escapeTable($this->table) . '} WHERE [collection] = :collection AND [name] = :name', [':collection' => $this->collection, ':name' => $name], $this->options)->fetchField();
+      $raw = $this->connection->query('SELECT [data] FROM {' . $this->connection->escapeTable($this->table) . '} WHERE [collection] = :collection AND [name] = :name ORDER BY collection, name', [
+        ':collection' => $this->collection,
+        ':name' => $name,
+      ], $this->options)->fetchField();
       if ($raw !== FALSE) {
         $data = $this->decode($raw);
       }
diff --git a/core/lib/Drupal/Core/Database/Driver/pgsql/Connection.php b/core/lib/Drupal/Core/Database/Driver/pgsql/Connection.php
index 0ffafc9e..64334244 100644
--- a/core/lib/Drupal/Core/Database/Driver/pgsql/Connection.php
+++ b/core/lib/Drupal/Core/Database/Driver/pgsql/Connection.php
@@ -351,6 +351,46 @@ class Connection extends DatabaseConnection {
     }
   }

+  /**
+   * Retrieves a sequence name that is owned by the table and column.
+   *
+   * @param string $table
+   *   A table name that is not prefixed or quoted.
+   * @param string $column
+   *   The column name.
+   *
+   * @return string|null
+   *   The name of the sequence or NULL if it does not exist.
+   */
+  public function getSequence($table, $column) {
+    $args = [
+      ':table' => $this->prefixTables('{' . $table . '}'),
+      ':column' => $column,
+    ];
+    return $this
+      ->query("SELECT pg_get_serial_sequence(:table, :column)", $args)
+      ->fetchField();
+  }
+
+  /**
+   * Checks if a sequence exists.
+   *
+   * @param string $name
+   *   The fully-qualified sequence name.
+   *
+   * @return bool
+   *   TRUE if the sequence exists by the name.
+   *
+   * @see \Drupal\Core\Database\Connection::makeSequenceName()
+   */
+  public function sequenceExists($name) {
+    $args = [':name' => $name];
+    return (bool) $this
+//       ->query("SELECT c.relname FROM pg_class as c INNER JOIN pg_namespace as ns ON (c.relnamespace = ns.oid) WHERE c.relkind = 'S' AND c.relname = :name", $args)
+      ->query("SELECT c.relname FROM pg_class as c WHERE c.relkind = 'S' AND c.relname = :name", $args)
+      ->fetchField();
+  }
+
 }

 /**
diff --git a/core/lib/Drupal/Core/Database/Driver/pgsql/Schema.php b/core/lib/Drupal/Core/Database/Driver/pgsql/Schema.php
index 6cf47641..e71f288f 100644
--- a/core/lib/Drupal/Core/Database/Driver/pgsql/Schema.php
+++ b/core/lib/Drupal/Core/Database/Driver/pgsql/Schema.php
@@ -958,7 +958,7 @@ EOD;
       // not when altering. Because of that, the sequence needs to be created
       // and initialized by hand.
       $seq = $this->connection->makeSequenceName($table, $field_new);
-      $this->connection->query("CREATE SEQUENCE " . $seq);
+      $this->connection->query("CREATE SEQUENCE " . $seq . " OWNED BY {" . $table . "}." . $field_new);
       // Set sequence to maximal field value to not conflict with existing
       // entries.
       $this->connection->query("SELECT setval('" . $seq . "', MAX(\"" . $field . '")) FROM {' . $table . "}");
@@ -1016,6 +1016,27 @@ EOD;
     }
   }

+  /**
+   * Alters the ownership of a sequence.
+   *
+   * This is used for updating orphaned sequences.
+   * In a Postgres database, when the changeField function is called, the pgsql driver
+   *
+   * See issue https://www.drupal.org/project/drupal/issues/3028706
+   *
+   * @param string $table
+   *   The unquoted or prefixed table name.
+   * @param string $column
+   *   The column name for the sequence.
+   *
+   * @internal
+   */
+  public function updateSequenceOwnership($table, $column) {
+    $seq = $this->connection->makeSequenceName($table, $column);
+    $table_name = $this->connection->prefixTables('{' . $table . '}');
+    $this->connection->query('ALTER SEQUENCE ' . $seq . ' OWNED BY ' . $table_name . '.' . $column);
+  }
+
   /**
    * Retrieve a table or column comment.
    */
diff --git a/core/modules/system/system.install b/core/modules/system/system.install
index a981accf..ebc9f24d 100644
--- a/core/modules/system/system.install
+++ b/core/modules/system/system.install
@@ -12,6 +12,7 @@ use Drupal\Component\Utility\Environment;
 use Drupal\Component\Utility\OpCodeCache;
 use Drupal\Component\Utility\Unicode;
 use Drupal\Core\Database\Database;
+use Drupal\Core\Database\DatabaseExceptionWrapper;
 use Drupal\Core\DrupalKernel;
 use Drupal\Core\Entity\Sql\SqlContentEntityStorage;
 use Drupal\Core\Entity\Sql\SqlContentEntityStorageSchema;
@@ -1523,3 +1524,122 @@ function system_update_8901() {
     }
   }
 }
+
+/**
+ * Fix any orphan sequences created from column changes in PostgreSQL.
+ */
+function system_update_9101(&$sandbox) {
+  $connection = \Drupal::database();
+  if ($connection->databaseType() !== 'pgsql') {
+    // This database update is a no-op for all other core database drivers.
+    $sandbox['#finished'] = 1;
+    return NULL;
+  }
+
+  if (!isset($sandbox['progress'])) {
+    $sandbox['fixed'] = 0;
+    $sandbox['tables'] = [];
+
+    // Discovers all custom tables with serial columns.
+    $module_handler = \Drupal::moduleHandler();
+    $modules = $module_handler->getModuleList();
+    foreach ($modules as $extension) {
+      $module = $extension->getName();
+      if ($module_handler->moduleExists($module)) {
+        module_load_include('install', $module);
+        $schema = $module_handler->invoke($module, 'schema');
+        if (!empty($schema)) {
+          foreach ($schema as $table_name => $table_info) {
+            foreach ($table_info['fields'] as $column_name => $column_info) {
+              if (substr($column_info['type'], 0, 6) === 'serial') {
+                $sandbox['tables'][] = [
+                  'table' => $table_name,
+                  'column' => $column_name,
+                ];
+              }
+            }
+          }
+        }
+      }
+    }
+
+    // Discovers all content entity types with integer entity keys that are most
+    // likely serial columns.
+    $entity_types = \Drupal::entityTypeManager()->getDefinitions();
+    // @var \Drupal\Core\Entity\EntityTypeInterface $entity_type
+    $sandbox['max'] = count($sandbox['tables']);
+    $sandbox['progress'] = 0;
+    foreach ($entity_types as $entity_type) {
+      $storage_class = $entity_type->getStorageClass();
+      if (is_subclass_of($storage_class, SqlContentEntityStorage::class)) {
+        $entity_class = $entity_type->getClass();
+        $id_key = $entity_type->getKey('id');
+        $revision_key = $entity_type->getKey('revision');
+
+        // @var \Drupal\Core\Field\BaseFieldDefinition[] $base_field_definitions
+        $base_field_definitions = $entity_class::baseFieldDefinitions($entity_type);
+        if ($base_field_definitions[$id_key]->getType() === 'integer') {
+          $sandbox['tables'][] = [
+            'table' => $entity_type->getBaseTable(),
+            'column' => $id_key,
+          ];
+        }
+
+        if ($entity_type->isRevisionable() &&
+          $base_field_definitions[$revision_key]->getType() === 'integer') {
+          $sandbox['tables'][] = [
+            'table' => $entity_type->getRevisionTable(),
+            'column' => $revision_key,
+          ];
+        }
+      }
+
+    }
+  }
+  else {
+    // Adds ownership of orphan sequences to tables.
+    $to_process = array_slice($sandbox['tables'], $sandbox['progress'], 50);
+
+    // Ensures that a sequence is not owned first, then ensures that the a
+    // sequence exists at all before trying to alter it.
+    foreach ($to_process as $table_info) {
+      if ($connection->schema()->tableExists($table_info['table'])) {
+        $owned = (bool) $connection->getSequence($table_info['table'], $table_info['column']);
+
+        if (!$owned) {
+          $sequence_name = $connection
+            ->makeSequenceName($table_info['table'], $table_info['column']);
+          $exists = $connection
+            ->sequenceExists($sequence_name);
+          if ($exists) {
+            $transaction = $connection->startTransaction($sequence_name);
+            try {
+              $connection
+                ->schema()
+                ->updateSequenceOwnership($table_info['table'], $table_info['column']);
+
+              $sandbox['fixed']++;
+            }
+            catch (DatabaseExceptionWrapper $e) {
+              $transaction->rollBack();
+            }
+          }
+        }
+      }
+
+      $sandbox['progress']++;
+    }
+  }
+
+  if ($sandbox['max'] && $sandbox['progress'] < $sandbox['max']) {
+    $sandbox['#finished'] = $sandbox['progress'] / $sandbox['max'];
+  }
+  else {
+    $sandbox['#finished'] = 1;
+
+    return \Drupal::translation()->formatPlural(
+      $sandbox['fixed'],
+      '1 orphaned sequence fixed.',
+      '@count orphaned sequences fixed');
+  }
+}
diff --git a/core/modules/system/tests/fixtures/update/drupal-8.pgsql-orphan-sequence.php b/core/modules/system/tests/fixtures/update/drupal-8.pgsql-orphan-sequence.php
new file mode 100644
index 00000000..4bbc92b2
--- /dev/null
+++ b/core/modules/system/tests/fixtures/update/drupal-8.pgsql-orphan-sequence.php
@@ -0,0 +1,46 @@
+<?php
+// @codingStandardsIgnoreFile
+
+use Drupal\Core\Database\Database;
+
+$connection = Database::getConnection();
+
+$db_type = $connection->databaseType();
+
+if ($db_type === 'pgsql') {
+  // Creates a table, then adds a sequence without ownership to simulate tables
+  // that were altered from integer to serial columns.
+  $connection
+    ->schema()
+    ->createTable('pgsql_sequence_test', [
+      'fields' => [
+        'sequence_field' => [
+          'type' => 'int',
+          'not null' => TRUE,
+          'unsigned' => TRUE,
+        ],
+      ],
+      'primary key' => ['sequence_field'],
+    ]);
+  $seq = $connection
+    ->makeSequenceName('pgsql_sequence_test', 'sequence_field');
+  $connection->query('CREATE SEQUENCE ' . $seq);
+
+  // Enables the pgsql_test module so that the pgsql_sequence_test schema will
+  // be available.
+  $extensions = $connection
+    ->query("SELECT data FROM {config} where name = 'core.extension'")
+    ->fetchField();
+  $extensions = unserialize($extensions);
+  $extensions['module']['pgsql_test'] = 1;
+
+  $connection
+    ->update('config')
+    ->fields(['data' => serialize($extensions)])
+    ->condition('name', 'core.extension')
+    ->execute();
+  $connection
+    ->delete('cache_config')
+    ->condition('cid', 'core.extension')
+    ->execute();
+}
diff --git a/core/modules/system/tests/modules/pgsql_test/pgsql_test.info.yml b/core/modules/system/tests/modules/pgsql_test/pgsql_test.info.yml
new file mode 100644
index 00000000..407e7761
--- /dev/null
+++ b/core/modules/system/tests/modules/pgsql_test/pgsql_test.info.yml
@@ -0,0 +1,6 @@
+name: 'PostgreSQL Tests'
+type: module
+description: 'Supports testing pgsql driver.'
+package: Testing
+version: VERSION
+
diff --git a/core/modules/system/tests/modules/pgsql_test/pgsql_test.install b/core/modules/system/tests/modules/pgsql_test/pgsql_test.install
new file mode 100644
index 00000000..e9f1577e
--- /dev/null
+++ b/core/modules/system/tests/modules/pgsql_test/pgsql_test.install
@@ -0,0 +1,24 @@
+<?php
+
+/**
+ * @file
+ * Install, update and uninstall functions for the pgsql_test module.
+ */
+
+/**
+ * Implements hook_schema().
+ */
+function pgsql_test_schema() {
+  $schema['pgsql_sequence_test'] = [
+    'description' => 'Test sequence changes on pgsql driver.',
+    'fields' => [
+      'sequence_field' => [
+        'type' => 'serial',
+        'not null' => TRUE,
+        'description' => 'Primary Key: A serial integer field.',
+      ],
+    ],
+    'primary key' => ['sequence_field'],
+  ];
+  return $schema;
+}
diff --git a/core/modules/system/tests/src/Functional/Database/PostgreSqlSequenceUpdateTest.php b/core/modules/system/tests/src/Functional/Database/PostgreSqlSequenceUpdateTest.php
new file mode 100644
index 00000000..8d73cd61
--- /dev/null
+++ b/core/modules/system/tests/src/Functional/Database/PostgreSqlSequenceUpdateTest.php
@@ -0,0 +1,47 @@
+<?php
+
+namespace Drupal\Tests\system\Functional\Database;
+
+use Drupal\FunctionalTests\Update\UpdatePathTestBase;
+
+/**
+ * Tests that any unowned sequences created previously have a table owner.
+ *
+ * The update path only applies to Drupal sites using the pgsql driver.
+ *
+ * @group Database
+ */
+class PostgreSqlSequenceUpdateTest extends UpdatePathTestBase {
+
+  /**
+   * {@inheritdoc}
+   */
+  protected function setDatabaseDumpFiles() {
+    $this->databaseDumpFiles = [
+      __DIR__ . '/../../../fixtures/update/drupal-8.8.0.bare.standard.php.gz',
+      __DIR__ . '/../../../fixtures/update/drupal-8.pgsql-orphan-sequence.php',
+    ];
+  }
+
+  /**
+   * Asserts that a newly created sequence has the correct ownership.
+   */
+  public function testPostgreSqlSequenceUpdate() {
+    $database = $this->container->get('database');
+    $db_type = $database->databaseType();
+
+    // Run the updates.
+    $this->runUpdates();
+
+    if ($db_type === 'pgsql') {
+      $sequence_exists = (bool) $database
+        ->query("SELECT pg_get_serial_sequence('{" . $database->prefixTables('pgsql_sequence_test') . "}', 'sequence_field')")
+        ->fetchField();
+      $this->assertTrue($sequence_exists, 'Sequence is owned by the table and column.');
+    }
+    else {
+      $this->assertTrue(TRUE, 'Database update ran successfully on non-pgsql driver.');
+    }
+  }
+
+}
diff --git a/core/tests/Drupal/KernelTests/Core/Database/SchemaTest.php b/core/tests/Drupal/KernelTests/Core/Database/SchemaTest.php
index 8dce4204..884a4ed2 100644
--- a/core/tests/Drupal/KernelTests/Core/Database/SchemaTest.php
+++ b/core/tests/Drupal/KernelTests/Core/Database/SchemaTest.php
@@ -56,6 +56,9 @@ class SchemaTest extends KernelTestBase {
    * Tests database interactions.
    */
   public function testSchema() {
+    // Tests for indexes are Database specific.
+    $db_type = $this->connection->databaseType();
+
     // Try creating a table.
     $table_specification = [
       'description' => 'Schema table description may contain "quotes" and could be long—very long indeed.',
@@ -94,7 +97,7 @@ class SchemaTest extends KernelTestBase {
     // Assert that the column comment has been set.
     $this->checkSchemaComment($table_specification['fields']['test_field']['description'], 'test_table', 'test_field');

-    if ($this->connection->databaseType() === 'mysql') {
+    if ($db_type === 'mysql') {
       // Make sure that varchar fields have the correct collation.
       $columns = $this->connection->query('SHOW FULL COLUMNS FROM {test_table}');
       foreach ($columns as $column) {
@@ -161,6 +164,13 @@ class SchemaTest extends KernelTestBase {

     // Change the new field to a serial column.
     $this->schema->changeField('test_table', 'test_serial', 'test_serial', ['type' => 'serial', 'not null' => TRUE, 'description' => 'Changed column description.'], ['primary key' => ['test_serial']]);
+    // Confirms that the new sequence is owned by the table in PostgreSQL.
+    if ($db_type == 'pgsql') {
+      $sequence_exists = (bool) $this->connection
+        ->query("SELECT pg_get_serial_sequence('{test_table}', 'test_serial')")
+        ->fetchField();
+      $this->assertTrue($sequence_exists, 'New sequence is owned by its table.');
+    }

     // Assert that the column comment has been set.
     $this->checkSchemaComment('Changed column description.', 'test_table', 'test_serial');
