How to get the full domain allowlist for pulling Docker images
Corporate HTTP proxies and security gateways (Squid, Zscaler, corporate gateways) often block unknown destinations. When your CI/CD pipelines or Kubernetes cluster attempt to pull container images, they don’t just talk to a single registry domain – images are frequently stored in backing object stores on completely different hosts (for example, AWS S3 or Google Cloud Storage domains). If those domains aren’t allowlisted, every pull will hang. The rest of this article walks through a repeatable process for discovering every domain used when pulling your specific images so that you can build a minimal allowlist for your proxy.
Prepare your image list
Start by assembling the set of images you plan to use. Put one fully qualified image reference per line in a plain text file and ignore comment lines (those starting with #). For example:
registry.k8s.io/external-dns/external-dns:v0.14.2ghcr.io/kyverno/kyverno:v1.12.0quay.io/prometheus/node-exporter:v1.8.2or use following command if you already have images in k8s (in case you are migrating from one kubernetes to another):
kubectl get pods -A -o json | jq -r '.items[].spec.containers[].image' | sort -u
Include any mirrored or private registry endpoints you consume. Repeat this procedure for each platform (e.g. linux/amd64 and linux/arm64) because different architectures may come from different storage buckets or regions.
Pull images with verbose logging
crane from the go-containerregistry project can pull container images and print HTTP traffic in verbose mode. The script below loops over each line in your image file, pulls the image for a specific platform, and writes all HTTP requests and responses to a log file (out.log). The destination of the pull is /dev/null, so only network interactions are recorded:
#!/usr/bin/env bashset -uset -o pipefail
if [[ $# -ne 1 ]]; then echo "Usage: $0 <images-file>" exit 1fi
IMAGES_FILE="$1"
if [[ ! -f "$IMAGES_FILE" ]]; then echo "Error: file not found: $IMAGES_FILE" exit 1fi
ARCH="amd64"OS="linux"
LOG="out.log"
: > "$LOG"
TOTAL=$(grep -vc '^\s*$\|^\s*#' "$IMAGES_FILE")COUNT=0URL_COUNT=0FAIL_COUNT=0
echo "[*] Platform : ${OS}/${ARCH}"echo "[*] Images to scan: $TOTAL"echo
resolve_image() { local image="$1" local before after delta
[[ -z "$image" ]] && return [[ "$image" =~ ^# ]] && return
COUNT=$((COUNT + 1)) echo "[${COUNT}/${TOTAL}] Pulling $image"
crane pull \ --platform="${OS}/${ARCH}" \ --verbose \ "$image" /dev/null \ 2>>"$LOG"}
while read -r image; do resolve_image "$image"done < "$IMAGES_FILE"
echo "Completed"Run the script behind your proxy to force all HTTP requests through it. If you deploy multiple architectures, change ARCH accordingly or loop over architectures.
Examine the logs
The resulting out.log file interleaves request and response lines. A request entry includes a timestamp, direction arrow (—>), HTTP method and full URL. Subsequent lines include the Host header and other metadata. For example:
2025/12/20 12:20:31 --> GET https://prod-registry-k8s-io-eu-central-1.s3.dualstack.eu-central-1.amazonaws.com/containers/images/sha256:18041d7e351f5a1eaf4f54ab71bdb9aca5c104a50653da49c2c69edd2f46d565 [body redacted: omitting binary blobs from logs]2025/12/20 12:20:31 GET /containers/images/sha256:18041d7e351f5a1eaf4f54ab71bdb9aca5c104a50653da49c2c69edd2f46d565 HTTP/1.1Host: prod-registry-k8s-io-eu-central-1.s3.dualstack.eu-central-1.amazonaws.comUser-Agent: crane/0.20.7 go-containerregistry/(devel)Referer: https://registry.k8s.io/v2/external-dns/external-dns/blobs/sha256:18041d7e351f5a1eaf4f54ab71bdb9aca5c104a50653da49c2c69edd2f46d565Accept-Encoding: gzip
2025/12/20 12:20:31 <-- 200 https://prod-registry-k8s-io-eu-central-1.s3.dualstack.eu-central-1.amazonaws.com/containers/images/sha256:18041d7e351f5a1eaf4f54ab71bdb9aca5c104a50653da49c2c69edd2f46d565 (140.98475ms) [body redacted: omitting binary blobs from logs]2025/12/20 12:20:31 HTTP/1.1 200 OKContent-Length: 40334622Accept-Ranges: bytesContent-Type: binary/octet-streamDate: Sat, 20 Dec 2025 11:20:32 GMTEtag: "a32f7a524e8925d6477109e1a4ee6d2a-8"Last-Modified: Tue, 02 Sep 2025 09:37:17 GMTServer: AmazonS3X-Amz-Id-2: 4738bNwUTkuPUFgEmL8FHxIzbF1AfFMwBR5Ny3mOU0oBErx5i9/qlYU3Y/oWt80FGN+W0heUf3mqGapUQMvhj4bzp+8T7rtbczH86SmKxbo=X-Amz-Replication-Status: REPLICAX-Amz-Request-Id: BHYDQY4KTZR0T2X4X-Amz-Server-Side-Encryption: AES256X-Amz-Version-Id: EFFEydHI6sT.arWAQdr.XDbPkk0a7AZdThe important part is the hostname – in this case, prod-registry-k8s-io-eu-central-1.s3.dualstack.eu-central-1.amazonaws.com. Registries often store layers in cloud object storage; you need to allow these domains even though they differ from the registry root.
Extract the unique domains
Once the log file is populated, use this pipeline to extract just the hostnames and sort them:
cat out.log | grep http | awk '{print $5}' | awk -F '/' '{print $3}' | sort -uThis command filters lines containing http, isolates the URL field, splits on / to pick out the domain, and deduplicates the results. A run against the example images produced:
auth.docker.iocdn01.quay.ioeurope-west3-docker.pkg.devghcr.ioindex.docker.iomcr.microsoft.compkg-containers.githubusercontent.comprod-registry-k8s-io-eu-central-1.s3.dualstack.eu-central-1.amazonaws.comproduction.cloudflare.docker.comquay.ioreg.kyverno.ioregistry.k8s.iowesteurope.data.mcr.microsoft.comAdd these domains to your proxy configuration. Depending on your proxy’s capabilities, you may be able to allow wildcard subdomains rather than enumerating each entry.
Tips and caveats
• Run per platform: Different CPU architectures may fetch layers from different storage regions. Repeat the process for each architecture you use.• Include mirrors and caches: If you pull through private mirrors, caches or content delivery networks, include those endpoints in images.txt.• Rerun as your image list grows: The completeness of your allowlist is determined by the images you test. Whenever you introduce new images or update to new versions, regenerate the list.• Understand your proxy syntax: Some proxies support patterns like *.s3.amazonaws.com; others require exact domain names. Adjust the list accordingly.← Back to blog