The Error Message
You’ve finished your build and run docker push, only to be met with a frustrating wall of text. The process stops immediately with this message:
Error response from daemon: Get "https://registry.example.com/v2/": unauthorized: authentication required
Root Causes
Simply put, the registry doesn't recognize you. While it might feel like a bug, it's usually a configuration gap. The most frequent culprits are:
- Expired Sessions: Your login token has timed out. For example, AWS ECR tokens expire after exactly 12 hours.
- Missing Hostnames: You’re pushing a local tag (like
my-app:v1) instead of a fully qualified registry path. - Stale Credentials: The Docker credential helper is holding onto an old password or an outdated Personal Access Token (PAT).
- RBAC Restrictions: You are logged in, but your account lacks "Push" or "Maintainer" permissions for that specific repository.
How to Fix It
1. Force a Fresh Login
Don't assume your previous session is still active. Re-authenticating is the fastest way to refresh your local config.json. Run the login command for your specific registry:
docker login registry.example.com
If you are using a cloud provider, use their CLI to handle the heavy lifting. For AWS, run aws ecr get-login-password --region us-east-1 | docker login --username AWS --password-stdin <aws_account_id>.dkr.ecr.us-east-1.amazonaws.com. For GitLab, ensure you are using a Deploy Token or a PAT with write_registry scopes.
2. Correct Your Image Tags
Docker uses the image name to determine the destination. If your image is just named web-server:latest, Docker tries to push to Docker Hub by default. To target a private registry, the hostname must be part of the tag.
The wrong way:
docker push web-server:v1
The right way:
# First, tag it correctly
docker tag web-server:v1 registry.example.com/my-team/web-server:v1
# Then, push the specific tag
docker push registry.example.com/my-team/web-server:v1
3. Inspect Your Docker Config
Authentication tokens live in ~/.docker/config.json on Linux or %USERPROFILE%\.docker\config.json on Windows. Open this file to see if your registry is actually listed.
cat ~/.docker/config.json
A healthy config should look like this:
{
"auths": {
"registry.example.com": {
"auth": "ZXhhbXBsZTpwYXNzd29yZA=="
}
}
}
If the auths section is empty despite a "Login Succeeded" message, your credential helper might be intercepting the data. Try removing the credsStore line from the file to force Docker to write credentials locally for testing.
4. Verify Repository Permissions
Sometimes the authentication is successful, but the authorization fails. In platforms like Harbor or GitLab, you might have "Guest" or "Reporter" access, which allows pulling but blocks pushing. Ask your admin to verify that your user has Developer or Maintainer roles. Without these, the registry will reject your push even with a valid password.
Testing the Solution
Check if Docker recognizes your login by running docker info | grep -i "registry". To verify the push without sending a massive image, try pushing a tiny 5MB Alpine layer first:
docker pull alpine:latest
docker tag alpine:latest registry.example.com/my-project/test:latest
docker push registry.example.com/my-project/test:latest
If the progress bar appears, you're back in business.
Pro-Tips for CI/CD
Avoid using personal passwords in automated pipelines. Instead, use Service Accounts or Deploy Tokens with limited scopes. For added security, I recommend using a robust, 32-character generated password for these accounts. I often use this Password Generator because it runs locally in your browser and ensures symbols are included to satisfy strict registry requirements.
Lastly, if you are behind a corporate firewall, check your HTTP_PROXY settings. A misconfigured proxy can intercept your authentication handshake, leading to a false "unauthorized" report.

