# GitLab CI: ERROR: Job failed: failed to pull image "registry.example.com/app:latest" with specified policies [always]: access forbidden

**Code:** `failed to pull image (GitLab)` · **Category:** CI/CD · **Seen on:** GitLab CI, Docker

The runner could not pull the job's Docker image — authentication, a wrong name or tag, or a registry rate limit.

## Meaning

Private registries need credentials in the `DOCKER_AUTH_CONFIG` variable (or the runner's config). Docker Hub applies pull-rate limits to anonymous pulls from shared runners; use a registry mirror or authenticate. Tags may simply not exist.

## Common causes

- Private image without `DOCKER_AUTH_CONFIG` credentials
- Wrong image name or missing tag
- Docker Hub rate limit on shared runners
- Expired token
- Runner without network access to the registry

## Quick fix

1. Add `DOCKER_AUTH_CONFIG` (CI/CD variable) with the registry auth JSON
2. Verify the image and tag exist: `docker pull` locally
3. Use a dependency proxy or mirror to avoid rate limits

## Detailed fix by platform

### GitLab CI

1. `DOCKER_AUTH_CONFIG` value: `{"auths":{"registry.example.com":{"auth":"<base64 user:token>"}}}`
2. Use `$CI_DEPENDENCY_PROXY_GROUP_IMAGE_PREFIX/node:20` for Docker Hub images

## Code examples

### Pull through the dependency proxy

```yaml
build:
  image: ${CI_DEPENDENCY_PROXY_GROUP_IMAGE_PREFIX}/node:20
  script: npm ci && npm test
```

## How to diagnose

1. **Name** — Image and tag correct?
2. **Auth** — Credentials configured?
3. **Limit** — Rate-limit message in the log?

---
Source: [WhatIsMyError](https://whatismyerror.com/errors/cicd/gitlab-image-pull-failed)
