7 Tips to Make GitHub CLI (gh) Faster for Linux Automation

A cron job that runs GitHub CLI (gh) commands every few minutes can become slow, prompt for input, or hit GitHub’s API rate limits. Here’s how to optimize it with GH_TOKEN, GH_REPO, --json, --cache, and GraphQL batching.

Many of us add gh commands to Bash scripts that monitor open pull requests or failed workflow runs. These scripts work well initially, but as they run repeatedly, unnecessary API requests and command overhead can slow them down or trigger API rate limit exceeded errors.

The problem is that gh is designed for interactive terminal use as well as automation. Depending on the command and configuration, it may resolve stored credentials, detect the current repository, check for updates, or retrieve more data than a script needs. These extra operations can add up when a cron job runs every five minutes.

The following seven tips show how to make GitHub CLI scripts faster, more predictable, and less wasteful with API requests across Linux distributions. Use a recent version of GitHub CLI, and verify support for the --slurp flag before applying Tip 6.

1. Skip Interactive Login in Scripts with GH_TOKEN

Authentication is the first step, because every other optimization depends on gh running unattended. Credentials saved by gh auth login may rely on a system keyring or another credential store that is unavailable to a cron job.

Setting GH_TOKEN provides an explicit authentication method for scripts. GitHub CLI documents that this environment variable takes precedence over stored credentials and avoids the interactive login prompt.

Create a configuration file on Ubuntu and Debian using nano:

sudo mkdir -p /etc/gh-automation
sudo nano /etc/gh-automation/env

On Rocky Linux, AlmaLinux, and RHEL, minimal installs ship with vi rather than nano, so use vi there:

sudo mkdir -p /etc/gh-automation
sudo vi /etc/gh-automation/env

Add the following configuration:

# /etc/gh-automation/env
GH_TOKEN=github_pat_xxxxxxxxxxxxxxxxxxxx
GH_PROMPT_DISABLED=1
GH_NO_UPDATE_NOTIFIER=1
GH_NO_EXTENSION_UPDATE_NOTIFIER=1
GH_TELEMETRY=false
GH_REPO=tecmint/web-config

Here is what each variable does:

  • GH_TOKEN supplies the authentication token for GitHub API requests. Replace the placeholder with a token that has only the permissions your script requires.
  • GH_PROMPT_DISABLED prevents interactive prompts, making commands that require user input fail instead of waiting indefinitely.
  • GH_NO_UPDATE_NOTIFIER disables the CLI version update notification.<./li>
  • GH_NO_EXTENSION_UPDATE_NOTIFIER disables extension update notifications.
  • GH_TELEMETRY is not a documented GitHub CLI environment variable for disabling telemetry, so remove it rather than relying on it.
  • GH_REPO sets the default repository in OWNER/REPO format, allowing commands to run without repeatedly specifying the repository. Replace tecmint/web-config with your own repository.

Save the file and restrict access to the account that runs the cron job. For example, if the job runs as tecmint:

sudo chown tecmint:tecmint /etc/gh-automation/env
sudo chmod 600 /etc/gh-automation/env

At the top of your script, load the file with set -a, which exports every variable so gh can see it:

set -a
source /etc/gh-automation/env
set +a

The set -a command automatically exports variables assigned while the file is sourced, and set +a turns that behavior off afterward.

Important: Treat the token as a password. Never commit this file to Git or print its contents in logs. If the script runs under a different account, make sure that account can read the file securely.

2. Disable Update Notifications with GH_NO_UPDATE_NOTIFIER

With authentication handled, the next step is to eliminate unnecessary background checks. GitHub CLI can periodically check for a newer version and notify you when an update is available. Extension update notifications can add similar noise when scripts run repeatedly.

For a cron job, these notifications provide little value and may clutter the error output or cron mail. The environment variables configured in Tip 1 already disable them, so you do not need to repeat the settings in every script.

GH_NO_UPDATE_NOTIFIER=1
GH_NO_EXTENSION_UPDATE_NOTIFIER=1

Keep GitHub CLI updated separately through your distribution’s package manager or another controlled update process. This lets you test new versions before deploying them to production automation.

Note: Disabling update notifications does not update gh or disable GitHub API rate limits. It simply prevents the corresponding update notices from appearing during routine execution.
If this helped you run gh more cleanly from cron, share it with someone who still hardcodes GitHub tokens directly into scripts.

3. Skip Repository Detection with GH_REPO

Now that gh runs without interactive prompts, the next step is to make sure it knows which repository to query. Commands such as gh pr list normally detect the repository from the current directory’s Git remotes.

However, cron jobs often start in the user’s home directory, which may not be a Git repository. The command can then fail with an error such as fatal: not a git repository.

The GH_REPO variable configured in Tip 1 solves this problem by specifying the default repository explicitly. Your script can run from any working directory without relying on Git repository detection.

When a script needs to query multiple repositories, use the -R or --repo flag to override the default for an individual command:

gh run list -R tecmint/ansible-roles --status failure --limit 5

This command lists up to five failed workflow runs from tecmint/ansible-roles, regardless of the current working directory or the value of GH_REPO.

4. Fetch Only the Fields You Need with –json and –jq

Once gh knows which repository to query, reduce the amount of data your script processes. Without structured output, list commands display human-readable tables that scripts often parse with tools such as awk or cut.

These approaches can break when titles contain spaces or the output format changes.

The --json flag requests specific fields and returns structured JSON, while --jq filters that result directly within GitHub CLI. This avoids brittle table parsing and eliminates the need to launch a separate jq process for simple filtering.

For example, retrieve open pull requests with their numbers, titles, and authors:

gh pr list --state open --limit 100 --json number,title,author \
  --jq '.[] | "\(.number) \(.author.login) \(.title)"'

Example output:

412 ravisaive Add fail2ban jail for nginx
409 tecmint Rotate journald logs weekly

The query extracts only the fields your script needs and formats each pull request as a single line. Adjust the field list and filter to match your reporting requirements.

Also, set --limit deliberately. The default limit for gh pr list is 30 results, so specify a higher limit when your report needs more entries, or a lower one when you only need the latest few.

Keep in mind that --json improves output handling and can reduce unnecessary field retrieval, but it does not guarantee fewer API requests in every situation.

5. Cache Repeated API Responses with gh api –cache

The previous tip made output easier to process, and this one helps avoid repeated API calls. Some information, such as an organization’s repository list, changes infrequently, yet a cron job running every five minutes may request it each time.

GitHub CLI supports response caching with the --cache flag on gh api. Specify a duration such as 1 hour to reuse cached responses during that period instead of contacting GitHub for every identical request.

For example, retrieve an organization’s repository names and cache the response:

gh api orgs/tecmint/repos --paginate --cache 1h --jq '.[].full_name'

The --paginate flag retrieves additional pages, while --jq extracts each repository’s full name. Caching is useful when the organization’s repository list does not need to be perfectly up to date.

Use this option only when stale data is acceptable. A cached response may not reflect repositories added or removed during the cache window. If you need fresh data, clear the cache with:

gh config clear-cache

Remember that clearing the cache affects cached responses beyond this particular endpoint, so do it deliberately in production scripts.

6. Batch Many Lookups into One Request with gh api graphql

Caching helps with data that changes infrequently, but monitoring scripts often need fresh information from several repositories. A loop that runs gh pr list separately for ten repositories can generate ten API requests.

GraphQL lets you query multiple repositories in a single request. You can use aliases to give each repository a distinct name in the response, making the results easy to process.

For example, retrieve the number of open pull requests in two repositories:

gh api graphql -f query='
{
  web: repository(owner: "tecmint", name: "web-config") {
    pullRequests(states: OPEN) { totalCount }
  }
  ansible: repository(owner: "tecmint", name: "ansible-roles") {
    pullRequests(states: OPEN) { totalCount }
  }
}' --jq '.data | to_entries[] | "\(.key): \(.value.pullRequests.totalCount)"'

Output:

web: 3
ansible: 0

When one collection spans several pages, combine --paginate with --slurp, which fetches every page and wraps them into one JSON array, so your filter runs once over the complete result.

If you’re running gh in a loop over many repositories, share this with your team, because one GraphQL query can replace the whole loop.

7. Measure API Usage with GH_DEBUG and gh api rate_limit

After applying the earlier tips, it’s worth confirming they actually reduced the work your script does. Setting GH_DEBUG=api makes gh log every HTTP request to standard error, so you can count the requests a single command makes:

GH_DEBUG=api gh pr list --limit 5 2>&1 >/dev/null | grep -c '^\* Request to'

Because debug output formats can vary between CLI versions, verify that the pattern matches your installed version. If it returns zero, inspect the debug output directly before concluding that no requests were made:

GH_DEBUG=api gh pr list --limit 5 >/dev/null

To see how much of your hourly API allowance is left, query the rate limit endpoint, which doesn’t count against the limit itself:

gh api rate_limit --jq '.resources | {core, graphql}'

The output lists limit, used, remaining, and a reset timestamp for each bucket, and remaining is the number to watch.

The core bucket covers REST calls from gh api, while graphql covers most gh pr and gh issue commands. If you run gh in parallel with xargs -P, keep the job count small, because GitHub applies secondary rate limits to bursts of concurrent requests even when remaining is still high.

If you’ve ever lost a nightly report to API rate limit exceeded, share this with a colleague who runs gh from cron.
Conclusion

With these seven tips, your gh scripts authenticate without prompts, skip update checks and repository detection, request only the data they need, and send far fewer API calls through caching and GraphQL batching. The result is automation that runs faster, logs less noise, and stays well under GitHub’s rate limits.

If you’d like to turn these commands into reusable scripts with proper error handling, loops, and functions, our Bash Scripting for Beginners course on Pro Tecmint walks you through writing production-ready Bash scripts step by step.

We’d love to hear how you’re using gh in your own automation. Do you keep your token in an environment file, a systemd credential, or a secrets manager? Have you hit a secondary rate limit, and how did you work around it? Feel free to share your scripts or errors in the comments.

Two small things before you go

If something didn’t work on your setup, leave a comment and tell me which distro and version you’re on. I read every one, and most of these articles got better because a reader pointed out what was missing.

And if it did work, share it. TecMint has stayed free for 14 years because people pass these articles around, and that traffic is what pays for writing the next one.

Leave a comment
Get two free Linux courses

Learn the Linux basics with two free courses and join 34,000+ readers getting practical TecMint tutorials and selected Pro TecMint articles every week.

Pro TecMint
Get lifetime access for $99

If you want to go beyond the free tutorials, Pro gives you 18 courses, 5 ebooks, and every future course I add. One payment, lifetime access, no renewals.

Ravi Saive
Ravi Saive
Administrator
About the author

I'm Ravi Saive, an award-winning entrepreneur and founder of several successful 5-figure online businesses, including TecMint.com, GeeksMint.com, UbuntuMint.com, and the premium learning hub Pro.Tecmint.com.

Got Something to Say? Join the Discussion...

Thank you for taking the time to share your thoughts with us. We appreciate your decision to leave a comment and value your contribution to the discussion. It's important to note that we moderate all comments in accordance with our comment policy to ensure a respectful and constructive conversation.

Rest assured that your email address will remain private and will not be published or shared with anyone. We prioritize the privacy and security of our users.