Manage GitHub Actions Variables Safely with gh variable
You will finish with a repeatable way to create, inspect, scope and remove GitHub Actions configuration variables from a terminal. The examples use GitHub CLI 2.87.3, installed here as package gh.
The route
Jump straight to the step you need, or tick off Done means at the end.
Allow about fifteen minutes. You need an authenticated gh session and permission to manage variables in the target repository or organisation. This guide changes GitHub configuration, so read each variable name and scope before running a write or delete command. No elevated Linux privileges are needed.
1. Confirm the installed command and account
Start with read-only checks. They confirm the binary, version and account without changing GitHub:
$ gh --version
gh version 2.87.3 (2026-02-23)
$ gh auth status
$ gh variable --help
The top-level command accepts -R or --repo in the form [HOST/]OWNER/REPO. Without it, repository-level commands use the repository associated with the current directory. To remove that ambiguity, the examples below name OWNER/REPO explicitly.
Checkpoint: replace both placeholders with the repository and organisation you actually intend to manage:
$ REPO='OWNER/REPO'
$ ORG='ORGANISATION'
$ gh variable list --repo "$REPO"
For an authenticated repository with no variables, the list can be empty. A permission error means you should fix authentication or access before trying a write. Do not use sudo; it does not grant GitHub API access.
2. Create a repository variable
Variables are for non-sensitive configuration such as a deployment target, feature name or tool setting. GitHub renders them unmasked in workflow output, so do not put passwords, private keys or access tokens in a variable. Store sensitive values as Actions secrets instead.
Use --body when the value is known and contains no secret. This is a state-changing command:
$ gh variable set DEPLOY_TARGET \
--repo "$REPO" \
--body 'staging'
✓ Set deployment variable DEPLOY_TARGET for OWNER/REPO
The repository is the default scope, so omitting --env and --org is deliberate here. Running set again with the same name updates the variable rather than creating a second one. Treat that update as an overwrite and review the value first.
Verify the name without printing its value:
$ gh variable list --repo "$REPO" --json name,updatedAt
[{"name":"DEPLOY_TARGET","updatedAt":"2026-09-24T12:00:00Z"}]
The timestamp is illustrative and will differ. The installed CLI also exposes value through --json, but avoid displaying it in shared terminals or CI logs.
3. Use the variable in a workflow
Configuration variables are available through the vars context. A workflow can copy one into a step environment, where the command can use the resulting variable:
jobs:
deploy:
runs-on: ubuntu-latest
steps:
- name: Show selected target
run: printf 'target=%s\n' "$DEPLOY_TARGET"
env:
DEPLOY_TARGET: ${{ vars.DEPLOY_TARGET }}
Keep the value non-sensitive. If the same variable name exists at organisation, repository and environment levels, the lowest applicable level wins: environment, then repository, then organisation. An environment variable becomes available after the job declares that environment. If a variable is absent, a vars reference resolves to an empty string, so validate critical settings in the workflow before deploying.
4. Inspect a value only when necessary
Use get for one variable. The command below asks for only the value and is useful for a local check:
$ gh variable get DEPLOY_TARGET --repo "$REPO" --json value --jq '.value'
staging
Do not paste this command into a build log if the variable might later become sensitive. For an environment-level variable, add --env ENVIRONMENT. For an organisation-level variable, use --org "$ORG". The scope flag is not cosmetic: querying the wrong level can report a missing variable or a different value.
For a name-only audit, use:
$ gh variable list --repo "$REPO" --json name,visibility,updatedAt
[{"name":"DEPLOY_TARGET","visibility":"private","updatedAt":"2026-09-24T12:00:00Z"}]
5. Set an environment or organisation variable
Use an environment variable when the setting belongs to a deployment environment in one repository:
$ gh variable set DEPLOY_TARGET \
--repo "$REPO" \
--env staging \
--body 'staging'
✓ Set environment variable DEPLOY_TARGET for staging
Use an organisation variable when several repositories should share the setting. The default organisation visibility is private. Choose the access policy explicitly:
$ gh variable set RELEASE_CHANNEL \
--org "$ORG" \
--visibility selected \
--repos repo-one,repo-two \
--body 'stable'
✓ Set organization variable RELEASE_CHANNEL
For an organisation variable visible to every public and private repository, the documented choice is --visibility all. That is a broad access decision, so check the organisation policy before using it. --repos is for the selected policy and accepts a comma-separated repository list.
For multiple non-sensitive values, a dotenv-formatted file can be imported:
$ umask 077
$ cat > variables.env <<'EOF'
BUILD_PROFILE=release
DEPLOY_TARGET=staging
EOF
$ gh variable set --repo "$REPO" --env staging --env-file variables.env
$ rm -- variables.env
Review the file before importing it. The final command deletes it and is irreversible, so keep it only if you need an auditable local copy. Never put credentials in this file just because its name says env.
6. Recover from a wrong value or remove a variable
There is no local undo history for a variable. If a value is wrong, set the intended value again and verify it. If you need to remove the variable, first list the exact scope, then delete it. Deletion is irreversible from the CLI and can break workflows:
$ gh variable list --repo "$REPO" --json name,updatedAt
$ gh variable delete DEPLOY_TARGET --repo "$REPO"
✓ Deleted variable DEPLOY_TARGET from OWNER/REPO
$ gh variable get DEPLOY_TARGET --repo "$REPO"
HTTP 404: Variable not found (https://api.github.com/...)
exit status: 1
The final error confirms that the repository variable is gone. Output wording and the API URL can vary. For an environment variable add --env staging to both commands; for an organisation variable use --org "$ORG". Do not omit the scope when cleaning up a variable with the same name at more than one level.
7. Diagnose the common traps
It says the variable is missing. Check --repo, --env and --org first. A variable at one level is not automatically returned by a query aimed at another level.
The workflow sees an empty value. Confirm the spelling, the workflow's vars reference and any declared environment. A repository variable cannot override an environment variable with the same name.
A value appears in logs. That is expected for variables. Replace it with a secret, remove diagnostic printing, and rotate the exposed credential if one was stored there. Changing the variable name does not make a leaked credential safe.
The organisation command is denied. Organisation variables need suitable organisation permissions, and selected-repository access must match the organisation policy. Fix the account or token scope rather than retrying with elevated Linux privileges.
Done means
- You confirmed the installed GitHub CLI version and authenticated account.
- You chose repository, environment or organisation scope deliberately.
- You stored only non-sensitive configuration in variables.
- You verified a write with a name-focused list or a scoped get.
- You understand that duplicate names resolve to the lowest applicable scope.
- You have a recovery value before deleting or overwriting a variable.