diff --git a/doc/administration/geo/disaster_recovery/background_verification.md b/doc/administration/geo/disaster_recovery/background_verification.md
index a927450d80f917b994fa1ca4dd5cd39ad6682818..c9bc957d41fd0ed2d6340990c1802a9ac297e6a5 100644
--- a/doc/administration/geo/disaster_recovery/background_verification.md
+++ b/doc/administration/geo/disaster_recovery/background_verification.md
@@ -104,7 +104,10 @@ If the **primary** and **secondary** sites have a checksum verification mismatch
       select its name.
    1. On the project administration page, get the values in the **Storage name** and **Relative path** fields.
 
-1. On a **Gitaly node on the primary** site and a **Gitaly node on the secondary** site, go to the project's repository directory. If using Gitaly Cluster, [check that it is in a healthy state](../../gitaly/troubleshooting.md#check-cluster-health) prior to running these commands.
+1. On a **Gitaly node on the primary** site and a **Gitaly node on the secondary** site, go to the project's repository
+   directory. If using Gitaly Cluster,
+   [check that it is in a healthy state](../../gitaly/troubleshooting_gitaly_cluster.md#check-cluster-health) before
+   running these commands.
 
    The default path is `/var/opt/gitlab/git-data/repositories`. If `git_data_dirs`
    is customized, check the directory layout on your server to be sure:
diff --git a/doc/administration/gitaly/index.md b/doc/administration/gitaly/index.md
index 4744959ce136acc41966cdc8eb4c534ea80bca3a..7826b98a66363ad463a4e62f5568b5d905a11670 100644
--- a/doc/administration/gitaly/index.md
+++ b/doc/administration/gitaly/index.md
@@ -379,7 +379,7 @@ cause problems on some file systems. In this case, `54771` hashes to
 
 #### Identify repositories on disk
 
-Use the [`praefect metadata`](troubleshooting.md#view-repository-metadata) subcommand to:
+Use the [`praefect metadata`](troubleshooting_gitaly_cluster.md#view-repository-metadata) subcommand to:
 
 - Retrieve a repository's virtual storage and relative path from the metadata store. After you have the hashed storage path, you can use the Rails
   console to retrieve the project path.
diff --git a/doc/administration/gitaly/praefect.md b/doc/administration/gitaly/praefect.md
index 53ec384ad0c61a8735f6ceda4ac8a79cf96e2c8b..3a705196949c12e5342b17c37ec96d786033554d 100644
--- a/doc/administration/gitaly/praefect.md
+++ b/doc/administration/gitaly/praefect.md
@@ -197,7 +197,7 @@ The following options are available:
      [Relational Database Service](https://aws.amazon.com/rds/) is recommended.
 
 Setting up PostgreSQL creates empty Praefect tables. For more information, see the
-[relevant troubleshooting section](troubleshooting.md#relation-does-not-exist-errors).
+[relevant troubleshooting section](troubleshooting_gitaly_cluster.md#relation-does-not-exist-errors).
 
 #### Running GitLab and Praefect databases on the same server
 
@@ -287,7 +287,7 @@ praefect['configuration'] = {
 ```
 
 If you see Praefect database errors after configuring PostgreSQL, see
-[troubleshooting steps](troubleshooting.md#relation-does-not-exist-errors).
+[troubleshooting steps](troubleshooting_gitaly_cluster.md#relation-does-not-exist-errors).
 
 #### Reads distribution caching
 
@@ -1689,7 +1689,7 @@ To migrate existing clusters:
 
    - If downtime is unacceptable:
 
-      1. Determine which Gitaly node is [the current primary](troubleshooting.md#determine-primary-gitaly-node).
+      1. Determine which Gitaly node is [the current primary](troubleshooting_gitaly_cluster.md#determine-primary-gitaly-node).
 
       1. Comment out the secondary Gitaly nodes from the virtual storage's configuration in `/etc/gitlab/gitlab.rb`
       on all Praefect nodes. This ensures there's only one Gitaly node configured, causing both of the election
diff --git a/doc/administration/gitaly/troubleshooting.md b/doc/administration/gitaly/troubleshooting.md
index 6c355f79191ad891163303252e7051a648065fe1..c8e000ad9e11f6e78d4aee38f3c2de104300accb 100644
--- a/doc/administration/gitaly/troubleshooting.md
+++ b/doc/administration/gitaly/troubleshooting.md
@@ -4,18 +4,17 @@ group: Gitaly
 info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://handbook.gitlab.com/handbook/product/ux/technical-writing/#assignments
 ---
 
-# Troubleshooting Gitaly and Gitaly Cluster **(FREE SELF)**
+# Troubleshooting Gitaly **(FREE SELF)**
 
-Refer to the information below when troubleshooting Gitaly and Gitaly Cluster.
-
-## Troubleshoot Gitaly
+Refer to the information below when troubleshooting Gitaly. For information on troubleshooting Gitaly Cluster (Praefect),
+see [Troubleshooting Gitaly Cluster](troubleshooting_gitaly_cluster.md).
 
 The following sections provide possible solutions to Gitaly errors.
 
 See also [Gitaly timeout](../settings/gitaly_timeouts.md) settings,
 and our advice on [parsing the `gitaly/current` file](../logs/log_parsing.md#parsing-gitalycurrent).
 
-### Check versions when using standalone Gitaly servers
+## Check versions when using standalone Gitaly servers
 
 When using standalone Gitaly servers, you must make sure they are the same version
 as GitLab to ensure full compatibility:
@@ -24,7 +23,7 @@ as GitLab to ensure full compatibility:
 1. Select **Overview > Gitaly Servers**.
 1. Confirm all Gitaly servers indicate that they are up to date.
 
-### Find storage resource details
+## Find storage resource details
 
 You can run the following commands in a [Rails console](../operations/rails_console.md#starting-a-rails-console-session)
 to determine the available and used space on a Gitaly storage:
@@ -35,7 +34,7 @@ Gitlab::GitalyClient::ServerService.new("default").storage_disk_statistics
 Gitlab::GitalyClient::ServerService.new("<storage name>").disk_statistics
 ```
 
-### Use `gitaly-debug`
+## Use `gitaly-debug`
 
 The `gitaly-debug` command provides "production debugging" tools for Gitaly and Git
 performance. It is intended to help production engineers and support
@@ -47,7 +46,7 @@ To see the help page of `gitaly-debug` for a list of supported sub-commands, run
 gitaly-debug -h
 ```
 
-### Commits, pushes, and clones return a 401
+## Commits, pushes, and clones return a 401
 
 ```plaintext
 remote: GitLab: 401 Unauthorized
@@ -56,14 +55,14 @@ remote: GitLab: 401 Unauthorized
 You need to sync your `gitlab-secrets.json` file with your GitLab
 application nodes.
 
-### 500 and `fetching folder content` errors on repository pages
+## 500 and `fetching folder content` errors on repository pages
 
 `Fetching folder content`, and in some cases `500`, errors indicate
 connectivity problems between GitLab and Gitaly.
 Consult the [client-side gRPC logs](#client-side-grpc-logs)
 for details.
 
-### Client side gRPC logs
+## Client side gRPC logs
 
 Gitaly uses the [gRPC](https://grpc.io/) RPC framework. The Ruby gRPC
 client has its own log file which may contain helpful information when
@@ -90,7 +89,7 @@ Check whether `Verify return code` field indicates a
 If `openssl` succeeds but `gitlab-rake gitlab:gitaly:check` fails,
 check [certificate requirements](tls_support.md#certificate-requirements) for Gitaly.
 
-### Server side gRPC logs
+## Server side gRPC logs
 
 gRPC tracing can also be enabled in Gitaly itself with the `GODEBUG=http2debug`
 environment variable. To set this in a Linux package installation:
@@ -105,7 +104,7 @@ environment variable. To set this in a Linux package installation:
 
 1. [Reconfigure](../restart_gitlab.md#reconfigure-a-linux-package-installation) GitLab.
 
-### Correlating Git processes with RPCs
+## Correlating Git processes with RPCs
 
 Sometimes you need to find out which Gitaly RPC created a particular Git process.
 
@@ -123,7 +122,7 @@ sudo cat /proc/$PID/environ | tr '\0' '\n' | grep ^CORRELATION_ID=
 This method isn't reliable for `git cat-file` processes, because Gitaly
 internally pools and re-uses those across RPCs.
 
-### Repository changes fail with a `401 Unauthorized` error
+## Repository changes fail with a `401 Unauthorized` error
 
 If you run Gitaly on its own server and notice these conditions:
 
@@ -224,7 +223,7 @@ the application might be fetching this secret from a different file. Your Gitaly
 If that setting is missing, GitLab defaults to using `.gitlab_shell_secret` under
 `/opt/gitlab/embedded/service/gitlab-rails/.gitlab_shell_secret`.
 
-### Repository pushes fail with `401 Unauthorized` and `JWT::VerificationError`
+## Repository pushes fail with `401 Unauthorized` and `JWT::VerificationError`
 
 When attempting `git push`, you can see:
 
@@ -246,7 +245,7 @@ From GitLab 15.5, GitLab [authenticates with GitLab Shell using a JWT token inst
 You should follow the [recommendations on upgrading external Gitaly](../../update/plan_your_upgrade.md#external-gitaly) and upgrade Gitaly before the GitLab
 server.
 
-### Repository pushes fail with a `deny updating a hidden ref` error
+## Repository pushes fail with a `deny updating a hidden ref` error
 
 Due to [a change](https://gitlab.com/gitlab-org/gitaly/-/merge_requests/3426)
 introduced in GitLab 13.12, Gitaly has read-only, internal GitLab references that users are not
@@ -268,7 +267,7 @@ git push origin +refs/heads/*:refs/heads/* +refs/tags/*:refs/tags/*
 
 Any other namespaces that the administrator wants to push can be included there as well via additional patterns.
 
-### Command-line tools cannot connect to Gitaly
+## Command-line tools cannot connect to Gitaly
 
 gRPC cannot reach your Gitaly server if:
 
@@ -307,7 +306,7 @@ unset http_proxy
 unset https_proxy
 ```
 
-### Permission denied errors appearing in Gitaly or Praefect logs when accessing repositories
+## Permission denied errors appearing in Gitaly or Praefect logs when accessing repositories
 
 You might see the following in Gitaly and Praefect logs:
 
@@ -328,14 +327,14 @@ This information in the logs is a gRPC call
 [error response code](https://grpc.github.io/grpc/core/md_doc_statuscodes.html).
 
 If this error occurs, even though
-[the Gitaly auth tokens are set up correctly](#praefect-errors-in-logs),
+[the Gitaly auth tokens are set up correctly](troubleshooting_gitaly_cluster.md#praefect-errors-in-logs),
 it's likely that the Gitaly servers are experiencing
 [clock drift](https://en.wikipedia.org/wiki/Clock_drift).
 
 Ensure the Gitaly clients and servers are synchronized, and use an NTP time
 server to keep them synchronized.
 
-### Gitaly not listening on new address after reconfiguring
+## Gitaly not listening on new address after reconfiguring
 
 When updating the `gitaly['configuration'][:listen_addr]` or `gitaly['configuration'][:prometheus_listen_addr]` values, Gitaly may
 continue to listen on the old address after a `sudo gitlab-ctl reconfigure`.
@@ -343,7 +342,7 @@ continue to listen on the old address after a `sudo gitlab-ctl reconfigure`.
 When this occurs, run `sudo gitlab-ctl restart` to resolve the issue. This should no longer be
 necessary because [this issue](https://gitlab.com/gitlab-org/gitaly/-/issues/2521) is resolved.
 
-### Permission denied errors appearing in Gitaly logs when accessing repositories from a standalone Gitaly node
+## Permission denied errors appearing in Gitaly logs when accessing repositories from a standalone Gitaly node
 
 If this error occurs even though file permissions are correct, it's likely that the Gitaly node is
 experiencing [clock drift](https://en.wikipedia.org/wiki/Clock_drift).
@@ -351,7 +350,7 @@ experiencing [clock drift](https://en.wikipedia.org/wiki/Clock_drift).
 Ensure that the GitLab and Gitaly nodes are synchronized and use an NTP time
 server to keep them synchronized if possible.
 
-### Health check warnings
+## Health check warnings
 
 The following warning in `/var/log/gitlab/praefect/current` can be ignored.
 
@@ -360,7 +359,7 @@ The following warning in `/var/log/gitlab/praefect/current` can be ignored.
 "msg":"error when looking up method info"
 ```
 
-### File not found errors
+## File not found errors
 
 The following errors in `/var/log/gitlab/gitaly/current` can be ignored.
 They are caused by the GitLab Rails application checking for specific files
@@ -372,7 +371,7 @@ that do not exist in a repository.
 "error":"not found: .gitlab-ci.yml"
 ```
 
-### Git pushes are slow when Dynatrace is enabled
+## Git pushes are slow when Dynatrace is enabled
 
 Dynatrace can cause the `/opt/gitlab/embedded/bin/gitaly-hooks` reference transaction hook,
 to take several seconds to start up and shut down. `gitaly-hooks` is executed twice when users
@@ -380,13 +379,13 @@ push, which causes a significant delay.
 
 If Git pushes are too slow when Dynatrace is enabled, disable Dynatrace.
 
-### `gitaly check` fails with `401` status code
+## `gitaly check` fails with `401` status code
 
 `gitaly check` can fail with `401` status code if Gitaly can't access the internal GitLab API.
 
 One way to resolve this is to make sure the entry is correct for the GitLab internal API URL configured in `gitlab.rb` with `gitlab_rails['internal_api_url']`.
 
-### Changes (diffs) don't load for new merge requests when using Gitaly TLS
+## Changes (diffs) don't load for new merge requests when using Gitaly TLS
 
 After enabling [Gitaly with TLS](tls_support.md), changes (diffs) for new merge requests are not generated
 and you see the following message in GitLab:
@@ -423,7 +422,7 @@ and:
 1. [Reconfigure GitLab](../restart_gitlab.md#reconfigure-a-linux-package-installation) so the certificates are symlinked
 1. Restart Gitaly manually `sudo gitlab-ctl restart gitaly` for the certificates to be loaded by the Gitaly process.
 
-### Gitaly fails to fork processes stored on `noexec` file systems
+## Gitaly fails to fork processes stored on `noexec` file systems
 
 Because of changes [introduced](https://gitlab.com/gitlab-org/omnibus-gitlab/-/merge_requests/5999) in GitLab 14.10, applying the `noexec` option to a mount
 point (for example, `/var`) causes Gitaly to throw `permission denied` errors related to forking processes. For example:
@@ -437,11 +436,11 @@ To resolve this, remove the `noexec` option from the file system mount. An alter
 1. Add `gitaly['runtime_dir'] = '<PATH_WITH_EXEC_PERM>'` to `/etc/gitlab/gitlab.rb` and specify a location without `noexec` set.
 1. Run `sudo gitlab-ctl reconfigure`.
 
-### Commit signing fails with `invalid argument: signing key is encrypted` or `invalid data: tag byte does not have MSB set.`
+## Commit signing fails with `invalid argument: signing key is encrypted` or `invalid data: tag byte does not have MSB set.`
 
 Because Gitaly commit signing is headless and not associated with a specific user, the GPG signing key must be created without a passphrase, or the passphrase must be removed before export.
 
-### Gitaly logs show errors in `info` messages
+## Gitaly logs show errors in `info` messages
 
 Because of a bug [introduced](https://gitlab.com/gitlab-org/gitaly/-/merge_requests/6201) in GitLab 16.3, additional entries were written to the
 [Gitaly logs](../logs/index.md#gitaly-logs). These log entries contained `"level":"info"` but the `msg` string appeared to contain an error.
@@ -458,312 +457,6 @@ safe to ignore.
 This bug was [fixed](https://gitlab.com/gitlab-org/gitaly/-/merge_requests/6513/) in GitLab 16.4.5, 16.5.5, and 16.6.0, which prevents these types of messages from
 being written to the Gitaly logs.
 
-## Troubleshoot Praefect (Gitaly Cluster)
-
-The following sections provide possible solutions to Gitaly Cluster errors.
-
-### Check cluster health
-
-> [Introduced](https://gitlab.com/gitlab-org/omnibus-gitlab/-/merge_requests/5688) in GitLab 14.5.
-
-The `check` Praefect sub-command runs a series of checks to determine the health of the Gitaly Cluster.
-
-```shell
-gitlab-ctl praefect check
-```
-
-The following sections describe the checks that are run.
-
-#### Praefect migrations
-
-Because Database migrations must be up to date for Praefect to work correctly, checks if Praefect migrations are up to date.
-
-If this check fails:
-
-1. See the `schema_migrations` table in the database to see which migrations have run.
-1. Run `praefect sql-migrate` to bring the migrations up to date.
-
-#### Node connectivity and disk access
-
-Checks if Praefect can reach all of its Gitaly nodes, and if each Gitaly node has read and write access to all of its storages.
-
-If this check fails:
-
-1. Confirm the network addresses and tokens are set up correctly:
-   - In the Praefect configuration.
-   - In each Gitaly node's configuration.
-1. On the Gitaly nodes, check that the `gitaly` process being run as `git`. There might be a permissions issue that is preventing Gitaly from
-   accessing its storage directories.
-1. Confirm that there are no issues with the network that connects Praefect to Gitaly nodes.
-
-#### Database read and write access
-
-Checks if Praefect can read from and write to the database.
-
-If this check fails:
-
-1. See if the Praefect database is in recovery mode. In recovery mode, tables may be read only. To check, run:
-
-   ```sql
-   select pg_is_in_recovery()
-   ```
-
-1. Confirm that the user that Praefect uses to connect to PostgreSQL has read and write access to the database.
-1. See if the database has been placed into read-only mode. To check, run:
-
-   ```sql
-   show default_transaction_read_only
-   ```
-
-#### Inaccessible repositories
-
-Checks how many repositories are inaccessible because they are missing a primary assignment, or their primary is unavailable.
-
-If this check fails:
-
-1. See if any Gitaly nodes are down. Run `praefect ping-nodes` to check.
-1. Check if there is a high load on the Praefect database. If the Praefect database is slow to respond, it can lead health checks failing to persist
-   to the database, leading Praefect to think nodes are unhealthy.
-
-#### Check clock synchronization
-
-> [Introduced](https://gitlab.com/gitlab-org/gitaly/-/merge_requests/4225) in GitLab 14.8.
-
-Authentication between Praefect and the Gitaly servers requires the server times to be
-in sync so the token check succeeds.
-
-This check helps identify the root cause of `permission denied`
-[errors being logged by Praefect](#permission-denied-errors-appearing-in-gitaly-or-praefect-logs-when-accessing-repositories).
-
-For offline environments where access to public `pool.ntp.org` servers is not possible, the Praefect `check` sub-command fails this
-check with an error message similar to:
-
-```plaintext
-checking with NTP service at  and allowed clock drift 60000ms [correlation_id: <XXX>]
-Failed (fatal) error: gitaly node at tcp://[gitlab.example-instance.com]:8075: rpc error: code = DeadlineExceeded desc = context deadline exceeded
-```
-
-To resolve this issue, set an environment variable on all Praefect servers to point to an accessible internal NTP server. For example:
-
-```shell
-export NTP_HOST=ntp.example.com
-```
-
-### Praefect errors in logs
-
-If you receive an error, check `/var/log/gitlab/gitlab-rails/production.log`.
-
-Here are common errors and potential causes:
-
-- 500 response code
-  - `ActionView::Template::Error (7:permission denied)`
-    - `praefect['configuration'][:auth][:token]` and `gitlab_rails['gitaly_token']` do not match on the GitLab server.
-  - `Unable to save project. Error: 7:permission denied`
-    - Secret token in `praefect['configuration'][:virtual_storage]` on GitLab server does not match the
-      value in `gitaly['auth_token']` on one or more Gitaly servers.
-- 503 response code
-  - `GRPC::Unavailable (14:failed to connect to all addresses)`
-    - GitLab was unable to reach Praefect.
-  - `GRPC::Unavailable (14:all SubCons are in TransientFailure...)`
-    - Praefect cannot reach one or more of its child Gitaly nodes. Try running
-      the Praefect connection checker to diagnose.
-
-### Praefect database experiencing high CPU load
-
-Some common reasons for the Praefect database to experience elevated CPU usage include:
-
-- Prometheus metrics scrapes [running an expensive query](https://gitlab.com/gitlab-org/gitaly/-/issues/3796). If you have GitLab 14.2
-  or above, set `praefect['configuration'][:prometheus_exclude_database_from_default_metrics] = true` in `gitlab.rb`.
-- [Read distribution caching](praefect.md#reads-distribution-caching) is disabled, increasing the number of queries made to the
-  database when user traffic is high. Ensure read distribution caching is enabled.
-
-### Determine primary Gitaly node
-
-To determine the primary node of a repository:
-
-- In GitLab 14.6 and later, use the [`praefect metadata`](#view-repository-metadata) subcommand.
-- In GitLab 13.12 to GitLab 14.5 with [repository-specific primaries](praefect.md#repository-specific-primary-nodes),
-  use the [`gitlab:praefect:replicas` Rake task](../raketasks/praefect.md#replica-checksums).
-- With legacy election strategies in GitLab 13.12 and earlier, the primary was the same for all repositories in a virtual storage.
-  To determine the current primary Gitaly node for a specific virtual storage:
-
-  - (Recommended) Use the `Shard Primary Election` [Grafana chart](praefect.md#grafana) on the
-    [`Gitlab Omnibus - Praefect` dashboard](https://gitlab.com/gitlab-org/grafana-dashboards/-/blob/master/omnibus/praefect.json).
-  - If you do not have Grafana set up, use the following command on each host of each
-    Praefect node:
-
-    ```shell
-    curl localhost:9652/metrics | grep gitaly_praefect_primaries
-    ```
-
-### View repository metadata
-
-> [Introduced](https://gitlab.com/gitlab-org/gitaly/-/issues/3481) in GitLab 14.6.
-
-Gitaly Cluster maintains a [metadata database](index.md#components) about the repositories stored on the cluster. Use the `praefect metadata` subcommand
-to inspect the metadata for troubleshooting.
-
-You can retrieve a repository's metadata by its Praefect-assigned repository ID:
-
-```shell
-sudo /opt/gitlab/embedded/bin/praefect -config /var/opt/gitlab/praefect/config.toml metadata -repository-id <repository-id>
-```
-
-When the physical path on the physical storage starts with `@cluster`, you can
-[find the repository ID in the physical path](index.md#praefect-generated-replica-paths-gitlab-150-and-later).
-
-You can also retrieve a repository's metadata by its virtual storage and relative path:
-
-```shell
-sudo /opt/gitlab/embedded/bin/praefect -config /var/opt/gitlab/praefect/config.toml metadata -virtual-storage <virtual-storage> -relative-path <relative-path>
-```
-
-#### Examples
-
-To retrieve the metadata for a repository with a Praefect-assigned repository ID of 1:
-
-```shell
-sudo /opt/gitlab/embedded/bin/praefect -config /var/opt/gitlab/praefect/config.toml metadata -repository-id 1
-```
-
-To retrieve the metadata for a repository with virtual storage `default` and relative path `@hashed/b1/7e/b17ef6d19c7a5b1ee83b907c595526dcb1eb06db8227d650d5dda0a9f4ce8cd9.git`:
-
-```shell
-sudo /opt/gitlab/embedded/bin/praefect -config /var/opt/gitlab/praefect/config.toml metadata -virtual-storage default -relative-path @hashed/b1/7e/b17ef6d19c7a5b1ee83b907c595526dcb1eb06db8227d650d5dda0a9f4ce8cd9.git
-```
-
-Either of these examples retrieve the following metadata for an example repository:
-
-```plaintext
-Repository ID: 54771
-Virtual Storage: "default"
-Relative Path: "@hashed/b1/7e/b17ef6d19c7a5b1ee83b907c595526dcb1eb06db8227d650d5dda0a9f4ce8cd9.git"
-Replica Path: "@hashed/b1/7e/b17ef6d19c7a5b1ee83b907c595526dcb1eb06db8227d650d5dda0a9f4ce8cd9.git"
-Primary: "gitaly-1"
-Generation: 1
-Replicas:
-- Storage: "gitaly-1"
-  Assigned: true
-  Generation: 1, fully up to date
-  Healthy: true
-  Valid Primary: true
-  Verified At: 2021-04-01 10:04:20 +0000 UTC
-- Storage: "gitaly-2"
-  Assigned: true
-  Generation: 0, behind by 1 changes
-  Healthy: true
-  Valid Primary: false
-  Verified At: unverified
-- Storage: "gitaly-3"
-  Assigned: true
-  Generation: replica not yet created
-  Healthy: false
-  Valid Primary: false
-  Verified At: unverified
-```
-
-#### Available metadata
-
-The metadata retrieved by `praefect metadata` includes the fields in the following tables.
-
-| Field             | Description                                                                                                        |
-|:------------------|:-------------------------------------------------------------------------------------------------------------------|
-| `Repository ID`   | Permanent unique ID assigned to the repository by Praefect. Different to the ID GitLab uses for repositories.      |
-| `Virtual Storage` | Name of the virtual storage the repository is stored in.                                                           |
-| `Relative Path`   | Repository's path in the virtual storage.                                                                          |
-| `Replica Path`    | Where on the Gitaly node's disk the repository's replicas are stored.                                                |
-| `Primary`         | Current primary of the repository.                                                                                 |
-| `Generation`      | Used by Praefect to track repository changes. Each write in the repository increments the repository's generation. |
-| `Replicas`        | A list of replicas that exist or are expected to exist.                                                            |
-
-For each replica, the following metadata is available:
-
-| `Replicas` Field | Description                                                                                                                                                                                                                                                                                                                                                                                                                                            |
-|:-----------------|:-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
-| `Storage`        | Name of the Gitaly storage that contains the replica.                                                                                                                                                                                                                                                                                                                                                                                                  |
-| `Assigned`       | Indicates whether the replica is expected to exist in the storage. Can be `false` if a Gitaly node is removed from the cluster or if the storage contains an extra copy after the repository's replication factor was decreased.                                                                                                                                                                                                                       |
-| `Generation`     | Latest confirmed generation of the replica. It indicates:<br><br>- The replica is fully up to date if the generation matches the repository's generation.<br>- The replica is outdated if the replica's generation is less than the repository's generation.<br>- `replica not yet created` if the replica does not yet exist at all on the storage.                                                                                                          |
-| `Healthy`        | Indicates whether the Gitaly node that is hosting this replica is considered healthy by the consensus of Praefect nodes.                                                                                                                                                                                                                                                                                                                               |
-| `Valid Primary`  | Indicates whether the replica is fit to serve as the primary node. If the repository's primary is not a valid primary, a failover occurs on the next write to the repository if there is another replica that is a valid primary. A replica is a valid primary if:<br><br>- It is stored on a healthy Gitaly node.<br>- It is fully up to date.<br>- It is not targeted by a pending deletion job from decreasing replication factor.<br>- It is assigned. |
-| `Verified At` | Indicates last successful verification of the replica by the [verification worker](praefect.md#repository-verification). If the replica has not yet been verified, `unverified` is displayed in place of the last successful verification time. Introduced in GitLab 15.0. |
-
-#### Command fails with 'repository not found'
-
-If the supplied value for `-virtual-storage` is incorrect, the command returns the following error:
-
-```plaintext
-get metadata: rpc error: code = NotFound desc = repository not found
-```
-
-The documented examples specify `-virtual-storage default`. Check the Praefect server setting `praefect['configuration'][:virtual_storage]` in `/etc/gitlab/gitlab.rb`.
-
-### Check that repositories are in sync
-
-Is [some cases](index.md#known-issues) the Praefect database can get out of sync with the underlying Gitaly nodes. To check that
-a given repository is fully synced on all nodes, run the [`gitlab:praefect:replicas` Rake task](../raketasks/praefect.md#replica-checksums) on your Rails node.
-This Rake task checksums the repository on all Gitaly nodes.
-
-The [Praefect `dataloss`](recovery.md#check-for-data-loss) command only checks the state of the repository in the Praefect database, and cannot
-be relied to detect sync problems in this scenario.
-
-### Relation does not exist errors
-
-By default Praefect database tables are created automatically by `gitlab-ctl reconfigure` task.
-
-However, the Praefect database tables are not created on initial reconfigure and can throw
-errors that relations do not exist if either:
-
-- The `gitlab-ctl reconfigure` command isn't executed.
-- Errors occur during the execution.
-
-For example:
-
-- `ERROR:  relation "node_status" does not exist at character 13`
-- `ERROR:  relation "replication_queue_lock" does not exist at character 40`
-- This error:
-
-  ```json
-  {"level":"error","msg":"Error updating node: pq: relation \"node_status\" does not exist","pid":210882,"praefectName":"gitlab1x4m:0.0.0.0:2305","time":"2021-04-01T19:26:19.473Z","virtual_storage":"praefect-cluster-1"}
-  ```
-
-To solve this, the database schema migration can be done using `sql-migrate` sub-command of
-the `praefect` command:
-
-```shell
-$ sudo /opt/gitlab/embedded/bin/praefect -config /var/opt/gitlab/praefect/config.toml sql-migrate
-praefect sql-migrate: OK (applied 21 migrations)
-```
-
-### Requests fail with 'repository scoped: invalid Repository' errors
-
-This indicates that the virtual storage name used in the
-[Praefect configuration](praefect.md#praefect) does not match the storage name used in
-[`gitaly['configuration'][:storage][<index>][:name]` setting](praefect.md#gitaly) for GitLab.
-
-Resolve this by matching the virtual storage names used in Praefect and GitLab configuration.
-
-### Gitaly Cluster performance issues on cloud platforms
-
-Praefect does not require a lot of CPU or memory, and can run on small virtual machines.
-Cloud services may place other limits on the resources that small VMs can use, such as
-disk IO and network traffic.
-
-Praefect nodes generate a lot of network traffic. The following symptoms can be observed if their network bandwidth has
-been throttled by the cloud service:
-
-- Poor performance of Git operations.
-- High network latency.
-- High memory use by Praefect.
-
-Possible solutions:
-
-- Provision larger VMs to gain access to larger network traffic allowances.
-- Use your cloud service's monitoring and logging to check that the Praefect nodes are not exhausting their traffic allowances.
-
-### `gitlab-ctl reconfigure` fails with error: `STDOUT: praefect: configuration error: error reading config file: toml: cannot store TOML string into a Go int`
-
-This error occurs when `praefect['database_port']` or `praefect['database_direct_port']` are configured as a string instead of an integer.
-
 ## Profiling Gitaly
 
 Gitaly exposes several of the Go built-in performance profiling tools on the Prometheus listen port. For example, if Prometheus is listening
diff --git a/doc/administration/gitaly/troubleshooting_gitaly_cluster.md b/doc/administration/gitaly/troubleshooting_gitaly_cluster.md
new file mode 100644
index 0000000000000000000000000000000000000000..9f1ae36b7425f9ad18706efd31d6226f4a1de01d
--- /dev/null
+++ b/doc/administration/gitaly/troubleshooting_gitaly_cluster.md
@@ -0,0 +1,312 @@
+---
+stage: Systems
+group: Gitaly
+info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://handbook.gitlab.com/handbook/product/ux/technical-writing/#assignments
+---
+
+# Troubleshooting Gitaly Cluster **(FREE SELF)**
+
+Refer to the information below when troubleshooting Gitaly Cluster (Praefect). For information on troubleshooting Gitaly,
+see [Troubleshooting Gitaly](troubleshooting.md).
+
+## Check cluster health
+
+> [Introduced](https://gitlab.com/gitlab-org/omnibus-gitlab/-/merge_requests/5688) in GitLab 14.5.
+
+The `check` Praefect sub-command runs a series of checks to determine the health of the Gitaly Cluster.
+
+```shell
+gitlab-ctl praefect check
+```
+
+The following sections describe the checks that are run.
+
+### Praefect migrations
+
+Because Database migrations must be up to date for Praefect to work correctly, checks if Praefect migrations are up to date.
+
+If this check fails:
+
+1. See the `schema_migrations` table in the database to see which migrations have run.
+1. Run `praefect sql-migrate` to bring the migrations up to date.
+
+### Node connectivity and disk access
+
+Checks if Praefect can reach all of its Gitaly nodes, and if each Gitaly node has read and write access to all of its storages.
+
+If this check fails:
+
+1. Confirm the network addresses and tokens are set up correctly:
+   - In the Praefect configuration.
+   - In each Gitaly node's configuration.
+1. On the Gitaly nodes, check that the `gitaly` process being run as `git`. There might be a permissions issue that is preventing Gitaly from
+   accessing its storage directories.
+1. Confirm that there are no issues with the network that connects Praefect to Gitaly nodes.
+
+### Database read and write access
+
+Checks if Praefect can read from and write to the database.
+
+If this check fails:
+
+1. See if the Praefect database is in recovery mode. In recovery mode, tables may be read only. To check, run:
+
+   ```sql
+   select pg_is_in_recovery()
+   ```
+
+1. Confirm that the user that Praefect uses to connect to PostgreSQL has read and write access to the database.
+1. See if the database has been placed into read-only mode. To check, run:
+
+   ```sql
+   show default_transaction_read_only
+   ```
+
+### Inaccessible repositories
+
+Checks how many repositories are inaccessible because they are missing a primary assignment, or their primary is unavailable.
+
+If this check fails:
+
+1. See if any Gitaly nodes are down. Run `praefect ping-nodes` to check.
+1. Check if there is a high load on the Praefect database. If the Praefect database is slow to respond, it can lead health checks failing to persist
+   to the database, leading Praefect to think nodes are unhealthy.
+
+### Check clock synchronization
+
+> [Introduced](https://gitlab.com/gitlab-org/gitaly/-/merge_requests/4225) in GitLab 14.8.
+
+Authentication between Praefect and the Gitaly servers requires the server times to be
+in sync so the token check succeeds.
+
+This check helps identify the root cause of `permission denied`
+[errors being logged by Praefect](troubleshooting.md#permission-denied-errors-appearing-in-gitaly-or-praefect-logs-when-accessing-repositories).
+
+For offline environments where access to public `pool.ntp.org` servers is not possible, the Praefect `check` sub-command fails this
+check with an error message similar to:
+
+```plaintext
+checking with NTP service at  and allowed clock drift 60000ms [correlation_id: <XXX>]
+Failed (fatal) error: gitaly node at tcp://[gitlab.example-instance.com]:8075: rpc error: code = DeadlineExceeded desc = context deadline exceeded
+```
+
+To resolve this issue, set an environment variable on all Praefect servers to point to an accessible internal NTP server. For example:
+
+```shell
+export NTP_HOST=ntp.example.com
+```
+
+## Praefect errors in logs
+
+If you receive an error, check `/var/log/gitlab/gitlab-rails/production.log`.
+
+Here are common errors and potential causes:
+
+- 500 response code
+  - `ActionView::Template::Error (7:permission denied)`
+    - `praefect['configuration'][:auth][:token]` and `gitlab_rails['gitaly_token']` do not match on the GitLab server.
+  - `Unable to save project. Error: 7:permission denied`
+    - Secret token in `praefect['configuration'][:virtual_storage]` on GitLab server does not match the
+      value in `gitaly['auth_token']` on one or more Gitaly servers.
+- 503 response code
+  - `GRPC::Unavailable (14:failed to connect to all addresses)`
+    - GitLab was unable to reach Praefect.
+  - `GRPC::Unavailable (14:all SubCons are in TransientFailure...)`
+    - Praefect cannot reach one or more of its child Gitaly nodes. Try running
+      the Praefect connection checker to diagnose.
+
+## Praefect database experiencing high CPU load
+
+Some common reasons for the Praefect database to experience elevated CPU usage include:
+
+- Prometheus metrics scrapes [running an expensive query](https://gitlab.com/gitlab-org/gitaly/-/issues/3796). If you have GitLab 14.2
+  or above, set `praefect['configuration'][:prometheus_exclude_database_from_default_metrics] = true` in `gitlab.rb`.
+- [Read distribution caching](praefect.md#reads-distribution-caching) is disabled, increasing the number of queries made to the
+  database when user traffic is high. Ensure read distribution caching is enabled.
+
+## Determine primary Gitaly node
+
+To determine the primary node of a repository:
+
+- In GitLab 14.6 and later, use the [`praefect metadata`](#view-repository-metadata) subcommand.
+- In GitLab 13.12 to GitLab 14.5 with [repository-specific primaries](praefect.md#repository-specific-primary-nodes),
+  use the [`gitlab:praefect:replicas` Rake task](../raketasks/praefect.md#replica-checksums).
+- With legacy election strategies in GitLab 13.12 and earlier, the primary was the same for all repositories in a virtual storage.
+  To determine the current primary Gitaly node for a specific virtual storage:
+
+  - (Recommended) Use the `Shard Primary Election` [Grafana chart](praefect.md#grafana) on the
+    [`Gitlab Omnibus - Praefect` dashboard](https://gitlab.com/gitlab-org/grafana-dashboards/-/blob/master/omnibus/praefect.json).
+  - If you do not have Grafana set up, use the following command on each host of each
+    Praefect node:
+
+    ```shell
+    curl localhost:9652/metrics | grep gitaly_praefect_primaries
+    ```
+
+## View repository metadata
+
+> [Introduced](https://gitlab.com/gitlab-org/gitaly/-/issues/3481) in GitLab 14.6.
+
+Gitaly Cluster maintains a [metadata database](index.md#components) about the repositories stored on the cluster. Use the `praefect metadata` subcommand
+to inspect the metadata for troubleshooting.
+
+You can retrieve a repository's metadata by its Praefect-assigned repository ID:
+
+```shell
+sudo /opt/gitlab/embedded/bin/praefect -config /var/opt/gitlab/praefect/config.toml metadata -repository-id <repository-id>
+```
+
+When the physical path on the physical storage starts with `@cluster`, you can
+[find the repository ID in the physical path](index.md#praefect-generated-replica-paths-gitlab-150-and-later).
+
+You can also retrieve a repository's metadata by its virtual storage and relative path:
+
+```shell
+sudo /opt/gitlab/embedded/bin/praefect -config /var/opt/gitlab/praefect/config.toml metadata -virtual-storage <virtual-storage> -relative-path <relative-path>
+```
+
+### Examples
+
+To retrieve the metadata for a repository with a Praefect-assigned repository ID of 1:
+
+```shell
+sudo /opt/gitlab/embedded/bin/praefect -config /var/opt/gitlab/praefect/config.toml metadata -repository-id 1
+```
+
+To retrieve the metadata for a repository with virtual storage `default` and relative path `@hashed/b1/7e/b17ef6d19c7a5b1ee83b907c595526dcb1eb06db8227d650d5dda0a9f4ce8cd9.git`:
+
+```shell
+sudo /opt/gitlab/embedded/bin/praefect -config /var/opt/gitlab/praefect/config.toml metadata -virtual-storage default -relative-path @hashed/b1/7e/b17ef6d19c7a5b1ee83b907c595526dcb1eb06db8227d650d5dda0a9f4ce8cd9.git
+```
+
+Either of these examples retrieve the following metadata for an example repository:
+
+```plaintext
+Repository ID: 54771
+Virtual Storage: "default"
+Relative Path: "@hashed/b1/7e/b17ef6d19c7a5b1ee83b907c595526dcb1eb06db8227d650d5dda0a9f4ce8cd9.git"
+Replica Path: "@hashed/b1/7e/b17ef6d19c7a5b1ee83b907c595526dcb1eb06db8227d650d5dda0a9f4ce8cd9.git"
+Primary: "gitaly-1"
+Generation: 1
+Replicas:
+- Storage: "gitaly-1"
+  Assigned: true
+  Generation: 1, fully up to date
+  Healthy: true
+  Valid Primary: true
+  Verified At: 2021-04-01 10:04:20 +0000 UTC
+- Storage: "gitaly-2"
+  Assigned: true
+  Generation: 0, behind by 1 changes
+  Healthy: true
+  Valid Primary: false
+  Verified At: unverified
+- Storage: "gitaly-3"
+  Assigned: true
+  Generation: replica not yet created
+  Healthy: false
+  Valid Primary: false
+  Verified At: unverified
+```
+
+### Available metadata
+
+The metadata retrieved by `praefect metadata` includes the fields in the following tables.
+
+| Field             | Description                                                                                                        |
+|:------------------|:-------------------------------------------------------------------------------------------------------------------|
+| `Repository ID`   | Permanent unique ID assigned to the repository by Praefect. Different to the ID GitLab uses for repositories.      |
+| `Virtual Storage` | Name of the virtual storage the repository is stored in.                                                           |
+| `Relative Path`   | Repository's path in the virtual storage.                                                                          |
+| `Replica Path`    | Where on the Gitaly node's disk the repository's replicas are stored.                                                |
+| `Primary`         | Current primary of the repository.                                                                                 |
+| `Generation`      | Used by Praefect to track repository changes. Each write in the repository increments the repository's generation. |
+| `Replicas`        | A list of replicas that exist or are expected to exist.                                                            |
+
+For each replica, the following metadata is available:
+
+| `Replicas` Field | Description                                                                                                                                                                                                                                                                                                                                                                                                                                            |
+|:-----------------|:-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
+| `Storage`        | Name of the Gitaly storage that contains the replica.                                                                                                                                                                                                                                                                                                                                                                                                  |
+| `Assigned`       | Indicates whether the replica is expected to exist in the storage. Can be `false` if a Gitaly node is removed from the cluster or if the storage contains an extra copy after the repository's replication factor was decreased.                                                                                                                                                                                                                       |
+| `Generation`     | Latest confirmed generation of the replica. It indicates:<br><br>- The replica is fully up to date if the generation matches the repository's generation.<br>- The replica is outdated if the replica's generation is less than the repository's generation.<br>- `replica not yet created` if the replica does not yet exist at all on the storage.                                                                                                          |
+| `Healthy`        | Indicates whether the Gitaly node that is hosting this replica is considered healthy by the consensus of Praefect nodes.                                                                                                                                                                                                                                                                                                                               |
+| `Valid Primary`  | Indicates whether the replica is fit to serve as the primary node. If the repository's primary is not a valid primary, a failover occurs on the next write to the repository if there is another replica that is a valid primary. A replica is a valid primary if:<br><br>- It is stored on a healthy Gitaly node.<br>- It is fully up to date.<br>- It is not targeted by a pending deletion job from decreasing replication factor.<br>- It is assigned. |
+| `Verified At` | Indicates last successful verification of the replica by the [verification worker](praefect.md#repository-verification). If the replica has not yet been verified, `unverified` is displayed in place of the last successful verification time. Introduced in GitLab 15.0. |
+
+### Command fails with 'repository not found'
+
+If the supplied value for `-virtual-storage` is incorrect, the command returns the following error:
+
+```plaintext
+get metadata: rpc error: code = NotFound desc = repository not found
+```
+
+The documented examples specify `-virtual-storage default`. Check the Praefect server setting `praefect['configuration'][:virtual_storage]` in `/etc/gitlab/gitlab.rb`.
+
+## Check that repositories are in sync
+
+Is [some cases](index.md#known-issues) the Praefect database can get out of sync with the underlying Gitaly nodes. To check that
+a given repository is fully synced on all nodes, run the [`gitlab:praefect:replicas` Rake task](../raketasks/praefect.md#replica-checksums) on your Rails node.
+This Rake task checksums the repository on all Gitaly nodes.
+
+The [Praefect `dataloss`](recovery.md#check-for-data-loss) command only checks the state of the repository in the Praefect database, and cannot
+be relied to detect sync problems in this scenario.
+
+## Relation does not exist errors
+
+By default Praefect database tables are created automatically by `gitlab-ctl reconfigure` task.
+
+However, the Praefect database tables are not created on initial reconfigure and can throw
+errors that relations do not exist if either:
+
+- The `gitlab-ctl reconfigure` command isn't executed.
+- Errors occur during the execution.
+
+For example:
+
+- `ERROR:  relation "node_status" does not exist at character 13`
+- `ERROR:  relation "replication_queue_lock" does not exist at character 40`
+- This error:
+
+  ```json
+  {"level":"error","msg":"Error updating node: pq: relation \"node_status\" does not exist","pid":210882,"praefectName":"gitlab1x4m:0.0.0.0:2305","time":"2021-04-01T19:26:19.473Z","virtual_storage":"praefect-cluster-1"}
+  ```
+
+To solve this, the database schema migration can be done using `sql-migrate` sub-command of
+the `praefect` command:
+
+```shell
+$ sudo /opt/gitlab/embedded/bin/praefect -config /var/opt/gitlab/praefect/config.toml sql-migrate
+praefect sql-migrate: OK (applied 21 migrations)
+```
+
+## Requests fail with 'repository scoped: invalid Repository' errors
+
+This indicates that the virtual storage name used in the
+[Praefect configuration](praefect.md#praefect) does not match the storage name used in
+[`gitaly['configuration'][:storage][<index>][:name]` setting](praefect.md#gitaly) for GitLab.
+
+Resolve this by matching the virtual storage names used in Praefect and GitLab configuration.
+
+## Gitaly Cluster performance issues on cloud platforms
+
+Praefect does not require a lot of CPU or memory, and can run on small virtual machines.
+Cloud services may place other limits on the resources that small VMs can use, such as
+disk IO and network traffic.
+
+Praefect nodes generate a lot of network traffic. The following symptoms can be observed if their network bandwidth has
+been throttled by the cloud service:
+
+- Poor performance of Git operations.
+- High network latency.
+- High memory use by Praefect.
+
+Possible solutions:
+
+- Provision larger VMs to gain access to larger network traffic allowances.
+- Use your cloud service's monitoring and logging to check that the Praefect nodes are not exhausting their traffic allowances.
+
+## `gitlab-ctl reconfigure` fails with error: `STDOUT: praefect: configuration error: error reading config file: toml: cannot store TOML string into a Go int`
+
+This error occurs when `praefect['database_port']` or `praefect['database_direct_port']` are configured as a string instead of an integer.
diff --git a/doc/administration/server_hooks.md b/doc/administration/server_hooks.md
index 562c8547d90eb88203a2e925bda8757e94846a1b..406964a117e497f872144575862e306550487f44 100644
--- a/doc/administration/server_hooks.md
+++ b/doc/administration/server_hooks.md
@@ -118,7 +118,7 @@ The location to copy the scripts to depends on where repositories are stored:
 - In GitLab 15.3 and later, new repositories are created using
   [Praefect-generated replica paths](gitaly/index.md#praefect-generated-replica-paths-gitlab-150-and-later),
   which are not the hashed storage path. The replica path can be identified by
-  [querying the Praefect repository metadata](../administration/gitaly/troubleshooting.md#view-repository-metadata)
+  [querying the Praefect repository metadata](../administration/gitaly/troubleshooting_gitaly_cluster.md#view-repository-metadata)
   using `-relative-path` to specify the expected GitLab hashed storage path.
 
 ## Create global server hooks for all repositories
diff --git a/doc/update/versions/gitlab_15_changes.md b/doc/update/versions/gitlab_15_changes.md
index 203d13a20e6d96c91e5bc670f1dd148cd838e4d0..c1eb034f7f529de70eb25ebb3cd2231fd96e917a 100644
--- a/doc/update/versions/gitlab_15_changes.md
+++ b/doc/update/versions/gitlab_15_changes.md
@@ -635,7 +635,7 @@ A [license caching issue](https://gitlab.com/gitlab-org/gitlab/-/issues/376706)
   creating, deleting, and renaming Git repositories.
 
   To identify the replica path,
-  [query the Praefect repository metadata](../../administration/gitaly/troubleshooting.md#view-repository-metadata)
+  [query the Praefect repository metadata](../../administration/gitaly/troubleshooting_gitaly_cluster.md#view-repository-metadata)
   and pass the `@hashed` storage path to `-relative-path`.
 
   With this information, you can correctly install