You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Describe the feature or problem you’d like to solve
The current way of creating PATs for authentication to private GHCR images is cumbersome. Especially if GHCR is adopted in an enterprise, it adds extra steps to employee onboarding.
Proposed solution
Add gh auth configure-docker (similar to how gcloud does it) and have Docker use gh as a credential helper for ghcr.io.
This is a great idea. PRs welcome! Tip: you can duplicate the existing gitcredential package as a starting point for the docker credential helper implementation.
$ dokcer login succeeds, but I can't seem to pull and push image from ghcr.io.
After investigating the cause, I found out that write:packages permission is required.
However, the token got from cfg.GetWithSource does not seem to have write:packages permission.
This is blocked on the fact that GitHub CLI's own OAuth token cannot be used to authenticate when pulling containers from ghcr.io: #5174 (comment)
Until this is solved on GitHub, gh auth configure-docker won't be possible. In the meantime, use manually-generated PATs with docker login ghcr.io --username <user> --password-stdin as before.
Reacted by Yuki Hirasawa, John Chavez, Dylan Arbour, Ereli and m0wer
You can gh auth login --scopes=write:packages, which at least adds that scope to the token.
After that, to make a Docker cred helper, you'll need to provide an executable named docker-credential-ghcr in $PATH, which expects to be passed the string "ghcr.io" on stdin, and produces the following JSON to stdout:
package main
import (
"encoding/json"
"io"
"log"
"os"
"strings"
"gopkg.in/yaml.v2"
)
func main() {
// Read auth token from config file.
b, err := os.ReadFile("/Users/jasonhall/.config/gh/hosts.yml")
if err != nil {
log.Fatal(err)
}
var m map[string]struct {
User string `yaml:"user"`
OAuthToken string `yaml:"oauth_token"`
}
if err := yaml.Unmarshal(b, &m); err != nil {
log.Fatal(err)
}
if _, ok := m["github.com"]; !ok {
log.Fatal("no github.com auth configured")
}
if got, err := io.ReadAll(os.Stdin); err != nil {
log.Fatal(err)
} else if strings.TrimSpace(string(got)) != "ghcr.io" {
log.Fatalf("bad input; got %q", string(got))
}
json.NewEncoder(os.Stdout).Encode(map[string]string{
"Username": m["github.com"].User,
"Secret": m["github.com"].OAuthToken,
})
}
That could either be a separate executable, or just a special mode of invoking gh if it can tell it's symlinked to $PATH/docker-credential-ghcr (based on os.Args[0]).
That symlink could be created as part of gh auth configure-docker, in addition to adding the "ghcr.io": "ghcr" line to "credHelpers" in ~/.docker/config.json, and maybe even upgrading the token to ensure it has the write:packages scope.
However, when I set all this up myself, I get the following error trying to write images to ghcr.io:
permission_denied: The token provided does not match expected scopes.
So it looks like there's still something missing to get the gh token to include the write:packages scope.
Is there some way to determine the scopes granted to a PAT, given only the PAT? Google OAuth supports a /tokeninfo endpoint which can confirm that the token includes the write:packages scope?
You can gh auth login --scopes=write:packages, which at least adds that scope to the token.
If there is progress on this, please make sure that as a user I can select read:packages as an option. I might want to only use this feature to read and not to write.
Does write:packages imply read:packages? If not, do you need to use --scopes=read:packages,write:packages or something for your use?
I'll rephrase, what does having a credential helper provide over and above logging in as in #5150 (comment) ?
It provides automatic credential handling/refresh across the whole docker ecosystem.
I.e. it works in docker, compose, all related tools and APIs.
And it also manages the lifecycle of the key through "store", "erase" protocol commands.
@pkit I don't think the lifecycle commands are necessary as they are managed by gh so they'd be noops like the GCR credential manager. But it would definitely be helpful to handle the secure storage and token refresh.
Reacted by André Roggeri Campos, David Collom, Johannes Rudolph, Jason Morganson, Ben Menesini, John HU, Rian Fowler, Dylan Arbour, Koki Nishihara and Hans
You can also pull the username automatically if you want to add this to a shared script
gh auth token | docker login ghcr.io --username $(gh api user --jq '.login') --password-stdin
Reacted by Mikael Elkiaer, Torsten Marco Lüdtke-Knodt, Julien Madiot, Max Schäfer, Samrid, ESASHIKA Kaoru, Lior Dux, Sterling Freeman, Rob Waight, Pedro Tôrres and 1 more
The suggestions above get the token and pass it to docker login, which stores a copy of the token in Docker's credential store.
This will work fine, but if you gh auth logout, gh auth switch, gh auth refresh --reset-scopes or gh auth login without --scopes read:packages, the token in Docker's cred store may not be the same as the one gh has, which may cause confusion.
The original request, to have gh auth token or similar called when Docker needs credentials to ghcr.io, would close this gap, since Docker's cred store wouldn't provide the token at all, and it would always delegate to gh.
It sounds like, in order to have gh be used as a cred helper smoothly, we'd also need gh's default set of scopes to include read:packages. If updating the default set of scopes is too cumbersome, perhaps gh auth configure-docker (name TBD) could request re-login with those scopes as part of setting itself up as a cred helper.
Reacted by Ruffin, Jason Stangroome, Maël Valais, Antti Harju, joe miller, Yuri Rocha - 유리 허샤 and Antoine Cotten
Activity
mislav commentedon Feb 1, 2022
Hi, thank you for the suggestion!
This is a great idea. PRs welcome! Tip: you can duplicate the existing
gitcredentialpackage as a starting point for the docker credential helper implementation.hirasawayuki commentedon Feb 3, 2022
I'd like to attempt to this issue! Can I be assigned to this issue?😀
hirasawayuki commentedon Feb 7, 2022
I have a question. Is there a way to get a token with write:packages permission?
I tried docker login using the token I got from
cfg.GetWithSource.cli/pkg/cmd/auth/configure-docker/configure_docker.go
Line 74 in c64940f
$ dokcer loginsucceeds, but I can't seem to pull and push image from ghcr.io.After investigating the cause, I found out that
write:packagespermission is required.However, the token got from
cfg.GetWithSourcedoes not seem to have write:packages permission.If you have a better way, please let me know 🙇♂️
mislav commentedon Feb 8, 2022
This is blocked on the fact that GitHub CLI's own OAuth token cannot be used to authenticate when pulling containers from
ghcr.io: #5174 (comment)Until this is solved on GitHub,
gh auth configure-dockerwon't be possible. In the meantime, use manually-generated PATs withdocker login ghcr.io --username <user> --password-stdinas before.srl295 commentedon Mar 18, 2022
Just for the record, this is no longer blocked on #5174, but that issue was folded into this one.
imjasonh commentedon Apr 4, 2022
You can
gh auth login --scopes=write:packages, which at least adds that scope to the token.After that, to make a Docker cred helper, you'll need to provide an executable named
docker-credential-ghcrin$PATH, which expects to be passed the string"ghcr.io"on stdin, and produces the following JSON to stdout:A minimal Go example of the above:
That could either be a separate executable, or just a special mode of invoking
ghif it can tell it's symlinked to$PATH/docker-credential-ghcr(based onos.Args[0]).That symlink could be created as part of
gh auth configure-docker, in addition to adding the"ghcr.io": "ghcr"line to"credHelpers"in~/.docker/config.json, and maybe even upgrading the token to ensure it has thewrite:packagesscope.However, when I set all this up myself, I get the following error trying to write images to ghcr.io:
So it looks like there's still something missing to get the
ghtoken to include thewrite:packagesscope.Is there some way to determine the scopes granted to a PAT, given only the PAT? Google OAuth supports a
/tokeninfoendpoint which can confirm that the token includes thewrite:packagesscope?srl295 commentedon Apr 4, 2022
@imjasonh or to whom it may concern,
If there is progress on this, please make sure that as a user I can select
read:packagesas an option. I might want to only use this feature to read and not to write.Does
write:packagesimplyread:packages? If not, do you need to use--scopes=read:packages,write:packagesor something for your use?28 remaining items
pkit commentedon Jan 11, 2024
It provides automatic credential handling/refresh across the whole docker ecosystem.
I.e. it works in docker, compose, all related tools and APIs.
And it also manages the lifecycle of the key through "store", "erase" protocol commands.
evilhamsterman commentedon Feb 20, 2024
@pkit I don't think the lifecycle commands are necessary as they are managed by gh so they'd be noops like the GCR credential manager. But it would definitely be helpful to handle the secure storage and token refresh.
baughmann commentedon Mar 30, 2024
Very helpful! Sidenote for me, this was actually
--usernamenot--user:gh auth token | docker login ghcr.io --username YOURUSER --password-stdinMattPChoy commentedon Dec 7, 2024
The above didn't work for me:
adrianisk commentedon Jan 7, 2025
You can also pull the username automatically if you want to add this to a shared script
MikaelElkiaer commentedon Jan 22, 2025
I just stumbled upon this: https://github.com/bradschwartz/docker-credential-ghcr-login
Can't believe how easy it would have been to implement.
Unfortunately, it is far more complicated for NuGet...
anttiharju commentedon Jan 9, 2026
Summarising what works as of writing:
babakks commentedon Jan 9, 2026
FWIW if you're using
v2.81.0or later, you can avoidgh apicall in favour ofgh auth status --jsonoutput:There'd be a API call under the hood (to validate the token), but it's a safer with multi-account setups.
imjasonh commentedon Jan 9, 2026
The suggestions above get the token and pass it to
docker login, which stores a copy of the token in Docker's credential store.This will work fine, but if you
gh auth logout,gh auth switch,gh auth refresh --reset-scopesorgh auth loginwithout--scopes read:packages, the token in Docker's cred store may not be the same as the oneghhas, which may cause confusion.The original request, to have
gh auth tokenor similar called when Docker needs credentials to ghcr.io, would close this gap, since Docker's cred store wouldn't provide the token at all, and it would always delegate togh.It sounds like, in order to have
ghbe used as a cred helper smoothly, we'd also needgh's default set of scopes to includeread:packages. If updating the default set of scopes is too cumbersome, perhapsgh auth configure-docker(name TBD) could request re-login with those scopes as part of setting itself up as a cred helper.anttiharju commentedon Jan 14, 2026
another example: https://edu.chainguard.dev/chainguard/chainctl/chainctl-docs/chainctl_auth_configure-docker/
MikaelElkiaer commentedon Jan 14, 2026
I still think this is a solid solution: https://github.com/MikaelElkiaer/docker-credential-ghcr-login
It is a real Docker credential helper that simply uses
ghcli - it even checks whether the required scope is set.anttiharju commentedon Jan 17, 2026
I'd be wary of unofficial solutions for anything auth-related.
chrheg commentedon Jan 29, 2026
You can use this as well: https://gist.github.com/mislav/e154d707db230dc882d7194ec85d79f6 , just a small shell script 👍