---
title: "Git HTTPS Access Token from an Environment Variable (Without Storing It)"
canonical: "https://techwiki.co.uk/general/git-https-token-from-environment-variable"
author: "Adam Birds"
article_type: "documentation"
published: "2026-10-24T23:27:47.258Z"
updated: "2026-10-24T23:27:47.258Z"
categories: ["General"]
tags: ["docker","git","github","python"]
---**To let a container, CI job or service pull and push over HTTPS with an access token, without the token ending up in the remote URL, `.git/config` or a credentials file, supply it through a credential helper defined entirely in environment variables.**

The usual shortcuts leak the token:

- `https://<api-token>@github.com/<owner>/<repo>.git` is saved in `.git/config` and appears in `git remote -v` output and some error messages.
- `git config credential.helper store` writes it in plain text to `~/.git-credentials`.

## How it works

Since Git 2.31, Git reads extra configuration from `GIT_CONFIG_COUNT`, `GIT_CONFIG_KEY_<n>` and `GIT_CONFIG_VALUE_<n>`. These settings apply only to processes that inherit the environment, and nothing is written to disk. A `credential.helper` value that starts with `!` is run as a shell snippet: Git calls it with `get` and reads `username=` and `password=` lines from its output.

## Fix

Set these variables in the environment of whatever runs Git:

```bash
export GIT_TOKEN="<api-token>"
export GIT_TERMINAL_PROMPT=0
export GIT_CONFIG_COUNT=2
export GIT_CONFIG_KEY_0=credential.helper
export GIT_CONFIG_VALUE_0=
export GIT_CONFIG_KEY_1=credential.helper
export GIT_CONFIG_VALUE_1='!f() { test "$1" = get || exit 0; echo username=x-access-token; echo "password=$GIT_TOKEN"; }; f'
```

What each part does:

- The empty first value clears any helpers inherited from the system or global config (such as a desktop keychain helper), so only this helper is asked.
- The helper refers to `$GIT_TOKEN` instead of containing the token, so `git config --list` never shows the secret.
- `test "$1" = get || exit 0` ignores the `store` and `erase` calls Git makes after a request succeeds or fails.
- `GIT_TERMINAL_PROMPT=0` makes Git fail instead of waiting for a password prompt that no one can answer.

The remote stays a plain URL:

```bash
git remote add origin https://github.com/<owner>/<repo>.git
git push origin main
```

### From a program

When a service runs Git as a subprocess, pass the variables in that subprocess's environment only:

```python
import os
import subprocess

HELPER = (
    '!f() { test "$1" = get || exit 0; '
    'echo "username=$GIT_USERNAME"; echo "password=$GIT_TOKEN"; }; f'
)

env = os.environ | {
    "GIT_TERMINAL_PROMPT": "0",
    "GIT_USERNAME": "x-access-token",
    "GIT_TOKEN": token,
    "GIT_CONFIG_COUNT": "2",
    "GIT_CONFIG_KEY_0": "credential.helper",
    "GIT_CONFIG_VALUE_0": "",
    "GIT_CONFIG_KEY_1": "credential.helper",
    "GIT_CONFIG_VALUE_1": HELPER,
}
result = subprocess.run(
    ["git", "push", "origin", "main"],
    cwd=repo_path, env=env, capture_output=True, text=True,
)
if result.returncode != 0:
    # Redact before logging, in case the token appears in an error.
    raise RuntimeError(result.stderr.replace(token, "***"))
```

## Verify

Ask Git which credentials it would use:

```bash
printf 'protocol=https\nhost=github.com\n\n' | git credential fill
```

The output should include `username=x-access-token` and the token as `password=`. Then confirm where the helper comes from and that nothing was stored:

```bash
git config --show-origin --get-all credential.helper   # helper listed with origin "command line:"
grep -c "<part-of-token>" .git/config                  # 0
```

## Notes

- Requires Git 2.31 or later. Tested with Git 2.43 and 2.54.
- For GitHub, a fine-grained personal access token limited to one repository, with Contents read and write, is enough to pull and push. GitHub should accept any non-empty username with a token; `x-access-token` is the conventional value.
- Every child process inherits these variables. If other code runs in the same environment, set them only on the Git subprocess, as in the Python example.
- For SSH remotes, the equivalent is a deploy key: write the private key to a file readable only by its owner and set `GIT_SSH_COMMAND="ssh -i <key-file> -o IdentitiesOnly=yes"`.
