Home / Alt manpages / gh-secret-set(1)

  • gh-secret-set(1)
  • User command
  • linux

Store a GitHub Secret Safely with gh secret set

Pasting a deploy token into a command line is how it ends up in your shell history, and then in someone's screenshot. This guide shows you how to create or update a GitHub secret with gh secret set from a Linux shell without printing its value, then verify it exists at the intended scope. Allow about ten minutes if you already have GitHub CLI authentication and permission to manage the target. The installed command here is GitHub CLI 2.87.3, so check the version on your own machine before relying on details in a script.

1. Check the command and your target

Start in the repository that should receive the secret, or decide which repository to name explicitly. You do not need root privileges, and sudo would not grant GitHub permission.

Warning

This command changes remote GitHub state, so do not use a real secret while testing.

$ gh --version
gh version 2.87.3 (2026-02-23)
$ gh secret set --help
Create or update secrets
$ gh auth status

The exact build date and authentication output vary.

Checkpoint

gh secret set --help shows the command, and gh auth status reports an account and host you recognise. If authentication fails, fix that first, and do not put a token into a shell command just to make the check pass.

2. Choose the secret scope

With no scope flag, gh secret set targets the current repository. Use --repo OWNER/REPO to remove any doubt about the destination.

  • Environment secret: add --env ENVIRONMENT as well as a repository.
  • Organisation secret: use --org ORGANISATION.
  • User secret: use --user. It is for Codespaces.

The application also matters for some secrets. The installed manual accepts actions, codespaces and dependabot with --app. Do not add an application flag just because a workflow happens to use a secret. Use it when the secret is intended for that application.

# Repository secret in the current repository
gh secret set DEPLOY_TOKEN

# Repository secret in an explicitly named repository
gh secret set DEPLOY_TOKEN --repo OWNER/REPOSITORY

# Environment secret
gh secret set DEPLOY_TOKEN --repo OWNER/REPOSITORY --env production

# Organisation secret for Dependabot
gh secret set REGISTRY_TOKEN --org ORGANISATION --app dependabot

Tip

These examples would prompt for a value and could change state. Treat them as command shapes only until you have chosen the real name and scope.

3. Enter one value without exposing it

The simplest interactive form is the safest starting point: omit --body and enter the value when prompted. Keep the terminal private and check that shell history does not contain the value. The command encrypts the secret locally before sending it to GitHub.

$ gh secret set DEPLOY_TOKEN --repo OWNER/REPOSITORY
? Paste your secret: ********
✓ Secret DEPLOY_TOKEN set

Prompt text and success symbols can differ between releases, so rely on a successful exit status. If you are updating an existing secret, the new value replaces the old one.

Warning

This command supplies no version history or undo. Retain a controlled recovery value only if your security policy permits it.

4. Supply a value from a protected shell variable

Use --body when another process has already provided a value, or read it silently from the terminal first. The variable is still present in the shell environment while the command runs, so do not export it broadly or leave it in a shared session.

$ read -r -s SECRET_VALUE
$ printf '\n'
$ gh secret set DEPLOY_TOKEN --repo OWNER/REPOSITORY --body "$SECRET_VALUE"
$ unset SECRET_VALUE

Quote the expansion, because an unquoted value can be split by the shell or expanded unexpectedly. If a CI system provides a masked variable, pass that variable according to the CI system's secret-handling rules and make sure debug tracing is disabled.

Warning

Avoid --body 'real-secret-value'. Command lines can be captured by history, process inspection or logs.

5. Load several secrets from a dotenv file

For several values, the command accepts a dotenv-formatted file with --env-file. Do not paste a real file into a ticket or chat transcript.

Security warning

A file containing secrets is sensitive. Restrict its permissions, keep it out of version control, and remove or securely retire it according to your retention policy after the import.

$ umask 077
$ touch /tmp/github-secrets.env
$ chmod 600 /tmp/github-secrets.env
$ editor /tmp/github-secrets.env
$ gh secret set --env-file /tmp/github-secrets.env --repo OWNER/REPOSITORY
$ rm /tmp/github-secrets.env

Populate the file in the dotenv format the command expects, with the actual secret names and values. Review the target and file path before running the import. Never put the file in a repository to make the import convenient.

Destructive action

The final rm cannot be undone. Run it only after confirming you no longer need the local copy.

6. Set organisation visibility deliberately

Organisation secrets have an access policy as well as a name and value. The installed command defaults --visibility to private. Use all for all repositories, or selected with --repos for a chosen list. --no-repos-selected creates an organisation secret that no repositories can access, which can help while you prepare a value but is not a usable workflow configuration.

# Selected repositories only
gh secret set REGISTRY_TOKEN --org ORGANISATION   --visibility selected --repos repository-one,repository-two

# Both public and private repositories
gh secret set REGISTRY_TOKEN --org ORGANISATION --visibility all

Warning

Visibility changes the set of repositories that can use the secret. Review the organisation name, application and repository list before pressing Enter. Narrowing access after an accidental broad upload does not make the value safe, so choose the policy before storing it.

7. Verify the name and scope

Secret values cannot be read back. Verify the metadata at the same scope instead, using gh secret list. It should show the secret name and an updated timestamp or equivalent metadata, but never the plaintext value.

$ gh secret list --repo OWNER/REPOSITORY

$ gh secret list --repo OWNER/REPOSITORY --env production

$ gh secret list --org ORGANISATION

$ gh secret list --user

Use the first command for a repository secret, the second for an environment secret, and the later commands for their matching scopes. If the name is absent, check the repository, environment, organisation, user and application flags before trying again. A workflow may still fail if its job cannot access that scope, if the environment requires approval, or if the secret name in the workflow differs.

8. Test encryption without storing a secret

When you want to inspect the encrypted representation without changing GitHub, use --no-store. It prints an encrypted, base64-encoded value instead of storing it. This is diagnostic output, not a replacement for a configured GitHub secret, and it is still sensitive enough to keep out of logs and tickets.

$ gh secret set TEST_VALUE --no-store
? Paste your secret: ********
BASE64_ENCRYPTED_VALUE

Do not confuse successful encryption with a successful workflow configuration. The normal command stores the value. --no-store deliberately does not.

Recovery and removal

Recovery

If you set the wrong value but still need the secret, run the same scoped command again with the corrected value.

If the secret must not exist, removal is irreversible from the CLI's point of view, so confirm the exact scope first:

$ gh secret list --repo OWNER/REPOSITORY
$ gh secret delete DEPLOY_TOKEN --repo OWNER/REPOSITORY

For an environment, organisation or user secret, repeat the matching --env, --org or --user option.

Warning

If a value may have been exposed in a command line, file, log or terminal recording, rotate it at the service that issued it as well as updating GitHub. Deleting the GitHub entry does not revoke the underlying credential.

Done means

  • Version and account checked. You confirmed the installed GitHub CLI version and authenticated account.
  • Scope selected. You chose the intended repository, environment, organisation or user scope.
  • Value kept out of history. It was entered interactively or passed through a protected variable, not written into command history.
  • Dotenv file handled. Any dotenv file was permission-restricted, excluded from version control and retired safely.
  • Name visible. gh secret list shows the expected name at the matching scope.
  • Way out known. You know how to replace or revoke the underlying credential if it was exposed.