| title | GitLab CI β Registry 401 Unauthorized on Image Pull/Push | |||||
|---|---|---|---|---|---|---|
| slug | gitlab-registry-401-unauthorized | |||||
| technologies |
|
|||||
| severity | high | |||||
| tags |
|
|||||
| related |
|
|||||
| last_reviewed | 2026-06-27 |
$ docker pull registry.gitlab.com/acme/app/base:latest
Error response from daemon: Head "https://registry.gitlab.com/v2/acme/app/base/manifests/latest": unauthorized: HTTP Basic: Access denied
denied: requested access to the resource is denied
unauthorized: authentication required
A 401 unauthorized from the GitLab Container Registry means the credentials the
job presented were rejected (or absent) when pulling or pushing an image. In CI
this usually surfaces while pulling a private base image for the job, or while
pushing a built image with docker push. The registry requires a valid token
scoped for the target repository; the default $CI_JOB_TOKEN works for the
current project but is restricted for cross-project access.
- gitlab (Container Registry, CI authentication)
high β builds that depend on private images cannot start, and release pipelines that push images fail, blocking deployment.
- The job never logged in to the registry, or used wrong credentials in
docker login. $CI_JOB_TOKENis being used to access a different project's registry and that project does not allow this project in its CI/CD job token allowlist.- A Personal/Project/Deploy token used for login is expired, revoked, or lacks
read_registry/write_registryscope. - The image path is wrong (typo in group/project/path), so auth is evaluated against a repo the token cannot access.
- Registry login happened in a different shell/layer than the pull/push (DinD credential not shared).
The GitLab Registry uses token-based auth: the Docker client requests a bearer
token from GitLab's auth service scoped to repository:<path>:pull (and/or
push). GitLab grants the token only if the presented credential has rights to
that path. $CI_JOB_TOKEN is automatically scoped to the running pipeline's
project; accessing another project's registry requires that project to allowlist
yours (Settings > CI/CD > Token Access). A revoked token or insufficient scope
yields no grant, and the registry responds 401 unauthorized / denied.
# Confirm the CI variables the job has for registry auth (names only, never echo secrets to public logs)
grep -nE 'CI_REGISTRY|CI_REGISTRY_IMAGE|docker login' .gitlab-ci.yml
# Inspect how the job authenticates
grep -nE 'docker login|DOCKER_AUTH_CONFIG|before_script' .gitlab-ci.yml
# Verify a token's scope via the API (read-only)
curl -s --header "PRIVATE-TOKEN: $GL_TOKEN" "https://gitlab.com/api/v4/personal_access_tokens/self" \
| tr ',' '\n' | grep -iE 'scopes|active|expires'
# Check the cross-project token allowlist setting for the target project
curl -s --header "PRIVATE-TOKEN: $GL_TOKEN" \
"https://gitlab.com/api/v4/projects/<target-id>/job_token_scope/allowlist"# Wrong/expired token: any pull returns
unauthorized: HTTP Basic: Access denied
# A correctly scoped login succeeds:
$ docker login -u "$CI_REGISTRY_USER" -p "$CI_REGISTRY_PASSWORD" "$CI_REGISTRY"
Login Succeeded
-
For the current project's registry, log in with the built-in variables:
before_script: - echo "$CI_REGISTRY_PASSWORD" | docker login -u "$CI_REGISTRY_USER" --password-stdin "$CI_REGISTRY"
-
For cross-project access via
$CI_JOB_TOKEN, add your project to the target project's CI/CD > Job token allowlist (and grant the right access level). -
If using a Deploy/Project Access Token, ensure it is active and has
read_registry(pull) and/orwrite_registry(push) scope; rotate if expired. -
Double-check the image path matches
$CI_REGISTRY_IMAGE/...exactly.
docker login -u "$CI_REGISTRY_USER" -p "$CI_REGISTRY_PASSWORD" "$CI_REGISTRY" # Login Succeeded
docker pull "$CI_REGISTRY_IMAGE/base:latest" # pulls without 401- Prefer
$CI_REGISTRY_USER/$CI_REGISTRY_PASSWORD(auto-scoped to the project) over hand-managed tokens. - Track token expiry; rotate Deploy/PATs before they lapse.
- Document and review the job-token allowlist when projects depend on each other.
gitlab Β· ci Β· registry Β· authentication Β· production