docker push returns denied, or docker pull fails with x509—usually the image is fine and the registry is not wired: service down, wrong repo type, no login, or the wrong hostname.
This article walks through AngusRepo Community self-hosted in order: install → repos and token → login → push → pull (including CI / Kubernetes). Examples use repo.example.com—swap your domain. Step-by-step denied: push denied troubleshooting.
1. Install (get a working console)
Prerequisites: Docker Engine + Compose v2; about 2 CPU / 4 GB; open 80; resolve gm.example.com and repo.example.com (labs can use /etc/hosts). Full steps: quickstart.
curl --fail --location --progress-bar -o AngusRepo-Community-1.0.0.zip \
https://repo.anguskit.com/raw/raw-public/AngusKit/repo/AngusRepo-Community-1.0.0.zip
unzip AngusRepo-Community-1.0.0.zip
cd AngusRepo-1.0.0
./install.sh
Wizard aligned with quickstart: Compose; Access → 2 (bundled proxy); TLS → 4 (HTTP :80); Database → Compose MySQL; Public DNS name → suffix such as example.com. After Install finished.:
./bin/angusctl.sh doctor # wait for doctor: OK
Open http://gm.example.com/, then http://repo.example.com/. Windows: curl.exe + .\install.bat. For HTTPS see Installation.
The rest assumes HTTP self-hosted, so lab Docker usually needs insecure-registries. Once the console opens, create repos and a token before docker login.
2. Repos, token, and image names
Push only to HOSTED; PROXY and GROUP are read-only. On http://repo.example.com/, create (names are examples—keep them consistent):
| Example name | Type | Role |
|---|---|---|
docker-hosted |
HOSTED | Sole docker push target |
dockerhub-proxy |
PROXY | Cache Docker Hub (https://registry-1.docker.io; Hub credentials recommended) |
docker-group (optional) |
GROUP | One pull entry over the two above |
Image form: host/repo/image:tag, e.g. repo.example.com/docker-hosted/myapp:1.0.0.
Do not hand-write /v2. Official Hub images via PROXY/GROUP need library/ (e.g. …/library/nginx:1.27).
Prefer console “connection config”; dedicated ports: Docker format manual.
Also prepare:
- [ ] Access token (shown once—save it)
- [ ] Push identity has Write on
docker-hosted; pull identity has Read on the entry repo
export REPO_USER="admin" # console username, not the token name
export REPO_TOKEN="your_access_token" # every password field uses the token
export REPO_HOST="repo.example.com" # your repo.<domain>
3. Login and certificates
HTTP (matches quickstart)
Without a certificate Docker still tries HTTPS and fails. Add the host to the daemon insecure list and restart:
{
"insecure-registries": ["repo.example.com"]
}
Then log in:
echo "$REPO_TOKEN" | docker login "$REPO_HOST" \
--username "$REPO_USER" \
--password-stdin
Prefer --password-stdin so tokens stay out of shell history.
After you move to HTTPS
Remove the host from insecure-registries and docker login again. Incomplete chains still produce x509: certificate signed by unknown authority.
| Symptom | Check first |
|---|---|
x509 / handshake failure |
Missing insecure-registries on HTTP; incomplete chain on HTTPS |
unauthorized / authentication required
|
Login done; password is the token; host is your private domain |
4. Push internal images
docker build -t myapp:1.0.0 .
docker tag myapp:1.0.0 "$REPO_HOST/docker-hosted/myapp:1.0.0"
docker push "$REPO_HOST/docker-hosted/myapp:1.0.0"
Target must be HOSTED. Pushing to GROUP or PROXY yields denied.
Same three steps in CI:
echo "$REPO_TOKEN" | docker login "$REPO_HOST" \
--username "$REPO_USER" --password-stdin
docker build -t "$REPO_HOST/docker-hosted/myapp:$VERSION" .
docker push "$REPO_HOST/docker-hosted/myapp:$VERSION"
Pipeline stages: Jenkins build, test, publish. Rejected pushes: push denied.
5. Pull images
# Internal
docker pull "$REPO_HOST/docker-hosted/myapp:1.0.0"
# Public base images via PROXY (note library/)
docker pull "$REPO_HOST/dockerhub-proxy/library/nginx:1.27"
# Unified entry if you created GROUP
docker pull "$REPO_HOST/docker-group/myapp:1.0.0"
docker pull "$REPO_HOST/docker-group/library/nginx:1.27"
Kubernetes
Nodes must resolve REPO_HOST. On HTTP registries, configure Docker / containerd on nodes to trust that host (same idea as insecure-registries).
kubectl create secret docker-registry repo-cred \
--docker-server="$REPO_HOST" \
--docker-username="$REPO_USER" \
--docker-password="$REPO_TOKEN" \
--namespace=default
apiVersion: apps/v1
kind: Deployment
metadata:
name: myapp
spec:
template:
spec:
imagePullSecrets:
- name: repo-cred
containers:
- name: myapp
image: repo.example.com/docker-hosted/myapp:1.0.0
6. Day-two ops (after push/pull works)
Tag cleanup — images grow fast. Keep the last N tags; drop *-dev / pr-* by name. Dry Run first—cleanup.
PROXY and rate limits — Hub anonymous pulls are about 100 / 6h. Put Hub credentials on dockerhub-proxy; pin versions in production; avoid latest.
Ingest checks — uploads get a light admission gate; deeper vuln/license work can use an external scanner or security product. Clear false positives before enabling block—admission gates and SBOM.
Error cheat sheet
| Error | Likely cause | Fix |
|---|---|---|
denied |
No Write, or push to GROUP/PROXY | Fix ACL; HOSTED only; push denied |
manifest unknown |
Wrong repo/image/tag; missing library/
|
Connection config and naming |
| Large pull timeout | Proxy timeout / body limit | Raise proxy_read_timeout and upload size |
| Login OK, push 401 | Expired token or stale login |
docker login again |
Field-level detail: Docker / OCI format manual.
FAQ
Can I push to a GROUP?
No. GROUP / PROXY are read-only; push only to HOSTED.
Should the image name include /v2?
No. The client adds the protocol path; putting it in the name breaks routing.
Is insecure-registries required on HTTP?
Yes. Without a certificate Docker defaults to HTTPS and usually fails with x509 or a handshake error. Remove it after you terminate real TLS.
Takeaways
- Install and open the console → create HOSTED (required), PROXY, optional GROUP → issue a token.
- After
docker login, push only to HOSTED; pull from HOSTED, PROXY, or GROUP. - HTTP labs need
insecure-registries; drop it on HTTPS; never hand-write/v2. - After the path works, set cleanup and PROXY credentials so disk and Hub limits do not stall builds.
Top comments (0)