Fix OrbStack Docker Pull
Core Rule
Treat Docker Hub EOF on OrbStack as a network path problem until proven otherwise. Do not assume the image is missing, private, or platform-incompatible before testing a small public image and the daemon's proxy path.
Expected responses that are not failures:
curl -I https://auth.docker.ioreturningHTTP/2 404is normal for the auth service root.curl -I https://registry-1.docker.ioreturningHTTP/2 404is normal for the registry root.curl -I https://registry-1.docker.io/v2/returning401withwww-authenticate: Bearer ...is the expected unauthenticated registry response.
Suspicious signals:
docker pull hello-world:latestfails withHead "https://registry-1.docker.io/v2/.../manifests/latest": EOF.docker buildx imagetools inspectordocker manifest inspectfails while explicitcurl -x http://127.0.0.1:<port>succeeds.registry-1.docker.ioresolves to198.18.0.x, which usually indicates Clash-style fake-ip/TUN routing.- macOS has system HTTP/HTTPS proxy enabled, but OrbStack Docker daemon is still using an unstable automatic proxy path.
Quick Diagnose
Run the bundled read-only diagnostic script first when local shell access is available:
/Users/jiamingfeng/.codex/skills/fix-orbstack-docker-pull/scripts/diagnose-orbstack-docker-pull.sh
If the skill has been installed elsewhere, resolve the script relative to this SKILL.md.
If running manually, collect these facts:
docker version
docker info
orb version
orb config get network_proxy
sed -n '1,120p' ~/.orbstack/config/docker.json
networksetup -getwebproxy Wi-Fi
networksetup -getsecurewebproxy Wi-Fi
dscacheutil -q host -a name registry-1.docker.io
dig registry-1.docker.io
netstat -rn -f inet | grep -E '198\\.18|default|utun'
curl -I --max-time 15 https://registry-1.docker.io/v2/
curl -x http://127.0.0.1:7890 -I --max-time 15 https://registry-1.docker.io/v2/
docker pull hello-world:latest
Adapt the proxy port from networksetup; do not hard-code 7890 unless the system proxy reports it.
Fix Workflow
- Confirm the issue is general:
docker pull hello-world:latest
If hello-world also fails with Docker Hub EOF, focus on daemon networking rather than the requested image.
- Find the active macOS proxy:
networksetup -getwebproxy Wi-Fi
networksetup -getsecurewebproxy Wi-Fi
ps aux | grep -Ei 'clash|cyberclash|mihomo|sing-box|surge|stash|v2ray|xray|shadow|proxy' | grep -v grep
If Server: 127.0.0.1 and Port: 7890 appear, the proxy URL is http://127.0.0.1:7890. Use the actual service name if the Mac is not using Wi-Fi.
- Make OrbStack use the proxy explicitly:
orb config set network_proxy http://127.0.0.1:<port>
orb restart docker
Verify:
orb config get network_proxy
docker pull hello-world:latest
- If pulls still intermittently EOF or large layers hang, reduce Docker daemon download concurrency:
{
"max-concurrent-downloads": 1
}
Write that JSON to ~/.orbstack/config/docker.json, preserving any existing daemon settings. Then restart:
orb restart docker
docker pull <image>
- Continue interrupted pulls instead of cleaning state:
docker pull <image>
docker system df
orb logs docker | tail -80
Docker often retains partially downloaded blobs after an interrupted pull. Avoid docker system prune unless the user explicitly wants to discard partial progress.
Interpretation Guide
registry-1.docker.ioresolving to198.18.0.xis a fake-ip/TUN clue, not the real Docker Hub endpoint.curl -x http://127.0.0.1:<port> -I https://registry-1.docker.io/v2/succeeding while direct curl or Docker fails means the explicit proxy path is healthier than auto/TUN routing.docker manifest inspectmay fail from the macOS-side client path even whendocker pullsucceeds through the daemon path; prioritizedocker pull hello-worldand the actual target pull for validation.- A long pause during a large layer can still be normal. Check
docker system dfgrowth or daemon logs before killing the pull. - If the proxy port changes after reboot, update OrbStack with the new port and restart Docker.
Persistence
These settings persist across Mac reboots:
orb config get network_proxy
sed -n '1,120p' ~/.orbstack/config/docker.json
Before future pulls, the user usually only needs OrbStack and the proxy app running. If the proxy app changes port, rerun:
orb config set network_proxy http://127.0.0.1:<new-port>
orb restart docker