diff --git a/src/backend/commands/publicationcmds.c b/src/backend/commands/publicationcmds.c
index 0c58ef6d5e5..575b296baaa 100644
--- a/src/backend/commands/publicationcmds.c
+++ b/src/backend/commands/publicationcmds.c
@@ -2241,11 +2241,21 @@ LockTablesWithoutReplicaIdentity(Form_pg_publication pubform, List *schemas)
 
 	/*
 	 * Serialize with concurrent publication descriptor builds.  ShareLock is
-	 * the weakest mode that conflicts with the RowExclusiveLock those take on
-	 * this catalog, and, unlike ShareRowExclusiveLock, it does not needlessly
-	 * serialize concurrent publication DDLs against each other.
+	 * the weakest mode that conflicts with the RowExclusiveLock those take,
+	 * and, unlike ShareRowExclusiveLock, it does not needlessly serialize
+	 * concurrent publication DDLs against each other.
+	 *
+	 * This uses a dedicated LockDatabaseObject() tag on pg_publication
+	 * (keyed by the reserved InvalidOid, which is never a real publication
+	 * OID, so it cannot collide with AlterPublication()'s own per-publication
+	 * lock on this same catalog) rather than LockRelationOid() on the
+	 * catalog's relation lock.  Reusing the relation lock would make this
+	 * wait collide with the ordinary RowExclusiveLock every unrelated
+	 * CREATE/ALTER/DROP PUBLICATION already takes to modify its own
+	 * pg_publication row, producing spurious deadlocks between publications
+	 * that share nothing but this catalog.
 	 */
-	LockRelationOid(PublicationRelationId, ShareLock);
+	LockDatabaseObject(PublicationRelationId, InvalidOid, 0, ShareLock);
 }
 
 /*
diff --git a/src/backend/utils/cache/relcache.c b/src/backend/utils/cache/relcache.c
index da99c3c62bc..3b93f9b0a02 100644
--- a/src/backend/utils/cache/relcache.c
+++ b/src/backend/utils/cache/relcache.c
@@ -5868,11 +5868,15 @@ RelationBuildPublicationDesc(Relation relation, PublicationDesc *pubdesc)
 
 	/*
 	 * Prevent concurrent publication DDL to publish the table lacking replica
-	 * identity (see LockTablesWithoutReplicaIdentity).
+	 * identity (see LockTablesWithoutReplicaIdentity). This uses a dedicated
+	 * LockDatabaseObject() tag on pg_publication, not LockRelationOid() on
+	 * the catalog's relation lock, so that this wait cannot collide with the
+	 * ordinary RowExclusiveLock any unrelated CREATE/ALTER/DROP PUBLICATION
+	 * already takes to modify its own pg_publication row.
 	 */
 	if (!OidIsValid(RelationGetReplicaIndex(relation)) &&
 		relation->rd_rel->relreplident != REPLICA_IDENTITY_FULL)
-		LockRelationOid(PublicationRelationId, RowExclusiveLock);
+		LockDatabaseObject(PublicationRelationId, InvalidOid, 0, RowExclusiveLock);
 
 	memset(pubdesc, 0, sizeof(PublicationDesc));
 	pubdesc->rf_valid_for_update = true;
