Home / Alt manpages / gh-repo-deploy-key-list(1)

  • gh-repo-deploy-key-list(1)
  • User command
  • linux

Audit GitHub Repository Deploy Keys with gh

gh repo deploy-key list is the fast way to prove exactly which deploy keys a GitHub repository has, and whether each one is read-only. You will finish able to save a focused JSON view for an audit, repeatably. The examples use GitHub CLI gh 2.87.3, the executable installed on this machine. The local package database reports gh 2.45.0-1ubuntu0.3+esm3, so check the executable you actually invoke when version-specific output matters.

Allow about ten minutes. You need GitHub CLI, a repository you are allowed to inspect, and an authenticated account or token with access to it. This guide only lists data: it does not add, rotate or delete a deploy key, and no elevated privilege is needed.

1. Confirm the executable and repository

Check which program will run, then confirm the repository name you intend to query. Use the full OWNER/REPO form in scripts so the result does not depend on the current directory:

$ command -v gh
/usr/bin/gh
$ gh --version
gh version 2.87.3 (2026-02-23)

Replace OWNER/REPO below with an existing repository, for example acme/widget. The optional HOST/ prefix supports a GitHub Enterprise host:

$ gh repo deploy-key list --repo OWNER/REPO

The command also accepts the short inherited spelling:

$ gh repo deploy-key list -R OWNER/REPO

Checkpoint

These two forms select the same target. Do not omit the owner when running from a directory that is not a Git checkout.

2. Read the normal list

Run the command without formatting flags for the standard human-readable list:

$ gh repo deploy-key list -R OWNER/REPO

The command lists the deploy keys GitHub returns for that repository. An empty result is a valid result: it means no keys are visible to the account for that repository. A failure such as an HTTP 404 can also mean the repository does not exist, the host is wrong, or your account cannot see it. Check the target and authentication before treating an empty list as proof that no keys exist.

Deploy keys are SSH public keys, but their presence grants access according to the key's repository permissions. Treat titles, fingerprints and access state as security-sensitive inventory: never paste a private key into a title, a ticket or a command line, and do not confuse a public deploy key shown here with the private key used by a build system.

3. Select fields for an audit record

Use --json when another tool needs structured output. This installed version exposes exactly these fields: createdAt, id, key, readOnly and title.

$ gh repo deploy-key list -R OWNER/REPO \
    --json id,title,key,readOnly,createdAt
[]

The example's [] is the valid JSON result for a repository with no visible deploy keys. With keys present, the result is an array of objects containing only the fields requested. Field order in the command is for readability; use names from the supported list rather than guessing at API fields.

The key field is useful for comparing a listed public key with an authorised inventory, but it can expose more detail than a basic report needs. Leave it out when a title, ID, access mode and creation time are enough:

$ gh repo deploy-key list -R OWNER/REPO \
    --json id,title,readOnly,createdAt

Checkpoint

Pipe this output to a file only after checking its destination and permissions. A local audit file may contain repository names and key material, even though the key value is public SSH data.

4. Filter the JSON without changing GitHub

--jq applies a jq expression to the JSON result. For example, print only keys that are marked read-only, with their title and creation time:

$ gh repo deploy-key list -R OWNER/REPO \
    --json title,readOnly,createdAt \
    --jq '.[] | select(.readOnly == true) | [.title, .createdAt] | @tsv'
build-read-only	2026-03-10T14:22:31Z

If the repository has no matching key, the filter prints nothing and normally exits successfully, which is easy to mistake for a failed request. Keep the unfiltered command or an ID-only JSON command nearby when you need to distinguish an empty list from a transport or permission error.

For a compact count in a shell check, use the array length:

$ gh repo deploy-key list -R OWNER/REPO \
    --json id \
    --jq 'length'
0

That count is a snapshot, not a lock or a continuous monitor. Another administrator can add or remove a key after the command completes.

5. Use templates only when their text format is useful

The command also accepts --template, using GitHub CLI's Go template formatting. A simple title and access-state report is suitable for a terminal or log:

$ gh repo deploy-key list -R OWNER/REPO \
    --json title,readOnly \
    --template '{{range .}}{{.title}}: read-only={{.readOnly}}{{"\n"}}{{end}}'
build-read-only: read-only=true

Use JSON rather than a template when another program will consume the result. Templates turn structured values into presentation text, and a title containing punctuation or a newline can make a log harder to parse.

Common traps

  • Not authenticated. Inspect the current account with gh auth status. If the repository is private or belongs to an organisation, ask its administrator for the minimum access needed to read repository administration data. Do not work around a permission error by copying a deploy key or using a personal token in shell history.
  • 404 response. Verify the exact owner, repository spelling and GitHub host. The -R or --repo value must be [HOST/]OWNER/REPO.
  • Missing JSON field. Rerun gh repo deploy-key list --help and use the fields listed under JSON FIELDS for the installed executable.
  • Saved output leaked key material. There is no undo step because listing is read-only; remove that report using your normal records-retention procedure, then check that no shell history, CI log or terminal capture retained it.

Done means

  • Confirmed the gh executable and selected the repository explicitly with -R or --repo.
  • Listed the visible deploy keys without changing repository state.
  • Requested only supported fields, including readOnly and createdAt.
  • Filtered the JSON with --jq and recognised that an empty result is not automatically an error.
  • Kept any saved inventory and public key values within the repository's security and retention rules.