Skip to content
How to get the full domain allowlist for pulling Docker images

How to get the full domain allowlist for pulling Docker images

December 20, 2025

Docker image pulls through a corporate proxy

Corporate HTTP proxies and security gateways (Squid, Zscaler and friends) love blocking unknown destinations. That’s a problem when your CI/CD pipeline or Kubernetes cluster pulls container images, because a “simple” pull talks to far more hosts than just the registry. Here is a repeatable way to find every single domain your images need, so you can build a minimal allowlist.

⚡
TL;DR — Problem: registries redirect layer downloads to object storage and CDNs on completely different hosts (S3, GCS, Cloudflare…), so pulls hang behind a strict proxy. Fix: pull every image with crane pull --verbose, then extract the unique hostnames from the log and allowlist them.

http-proxy

Why one registry domain is not enough

When you pull registry.k8s.io/external-dns/external-dns, the manifest comes from registry.k8s.io, but the blobs (the actual layers) are often served from a backing store such as prod-registry-k8s-io-eu-central-1.s3.dualstack.eu-central-1.amazonaws.com. If that host isn’t allowlisted, the pull just hangs.

Request flow of a container image pull through a proxy

Prepare your image list

Put one fully qualified image reference per line in a plain text file. Lines starting with # are ignored.

# images.txt
registry.k8s.io/external-dns/external-dns:v0.14.2
ghcr.io/kyverno/kyverno:v1.12.0
quay.io/prometheus/node-exporter:v1.8.2

If you are migrating from one Kubernetes cluster to another, you can dump the images already running there:

kubectl get pods -A -o json | jq -r '.items[].spec.containers[].image' | sort -u

Include any mirrored or private registry endpoints you consume.

Pull images with verbose logging

crane from the go-containerregistry project can pull images and print all HTTP traffic in verbose mode. The script below loops over your image file, pulls each image for one platform and appends every request and response to out.log. The pull destination is /dev/null, so only the network interaction matters.

Examine the logs

Find the hostnames that show up in the requests.

Extract the unique domains

Turn the log into a deduplicated list and add it to your proxy.

🧩
Repeat the whole procedure for each platform you run (e.g. linux/amd64 and linux/arm64). Different architectures may be served from different storage buckets or regions.

The pull script

#!/usr/bin/env bash
set -u
set -o pipefail

if [[ $# -ne 1 ]]; then
  echo "Usage: $0 <images-file>"
  exit 1
fi

IMAGES_FILE="$1"

if [[ ! -f "$IMAGES_FILE" ]]; then
  echo "Error: file not found: $IMAGES_FILE"
  exit 1
fi

ARCH="amd64"
OS="linux"

LOG="out.log"

: > "$LOG"

TOTAL=$(grep -vc '^\s*$\|^\s*#' "$IMAGES_FILE")
COUNT=0
URL_COUNT=0
FAIL_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 so all HTTP requests go through it. If you deploy multiple architectures, change ARCH accordingly or loop over the architectures.

Examine the logs

out.log interleaves requests and responses. A request entry has a timestamp, a direction arrow (-->), the HTTP method and the full URL, followed by the Host header and other metadata:

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.1
Host: prod-registry-k8s-io-eu-central-1.s3.dualstack.eu-central-1.amazonaws.com
User-Agent: crane/0.20.7 go-containerregistry/(devel)
Referer: https://registry.k8s.io/v2/external-dns/external-dns/blobs/sha256:18041d7e351f5a1eaf4f54ab71bdb9aca5c104a50653da49c2c69edd2f46d565
Accept-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 OK
Content-Length: 40334622
Accept-Ranges: bytes
Content-Type: binary/octet-stream
Date: Sat, 20 Dec 2025 11:20:32 GMT
Etag: "a32f7a524e8925d6477109e1a4ee6d2a-8"
Last-Modified: Tue, 02 Sep 2025 09:37:17 GMT
Server: AmazonS3
X-Amz-Id-2: 4738bNwUTkuPUFgEmL8FHxIzbF1AfFMwBR5Ny3mOU0oBErx5i9/qlYU3Y/oWt80FGN+W0heUf3mqGapUQMvhj4bzp+8T7rtbczH86SmKxbo=
X-Amz-Replication-Status: REPLICA
X-Amz-Request-Id: BHYDQY4KTZR0T2X4
X-Amz-Server-Side-Encryption: AES256
X-Amz-Version-Id: EFFEydHI6sT.arWAQdr.XDbPkk0a7AZd
🔑
The interesting part is the hostname: prod-registry-k8s-io-eu-central-1.s3.dualstack.eu-central-1.amazonaws.com. Note the Referer pointing back to registry.k8s.io: the registry redirected the blob download to S3. You must allow these storage domains even though they look nothing like the registry.

Extract the unique domains

Once the log is populated, this pipeline pulls out just the hostnames:

cat out.log | grep http | awk '{print $5}' | awk -F '/' '{print $3}' | sort -u

It keeps lines containing http, takes the URL field, splits it on / to get the domain and deduplicates the result. A run against the example images produced:

auth.docker.io
cdn01.quay.io
europe-west3-docker.pkg.dev
ghcr.io
index.docker.io
mcr.microsoft.com
pkg-containers.githubusercontent.com
prod-registry-k8s-io-eu-central-1.s3.dualstack.eu-central-1.amazonaws.com
production.cloudflare.docker.com
quay.io
reg.kyverno.io
registry.k8s.io
westeurope.data.mcr.microsoft.com

Add these domains to your proxy configuration. Depending on what your proxy supports, you may be able to allow wildcard subdomains instead of listing 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 CDNs, put those endpoints in images.txt too.
  • Rerun as your image list grows: the allowlist is only as complete as the images you test. Regenerate it whenever you add images or bump versions.
  • Know your proxy syntax: some proxies support patterns like *.s3.amazonaws.com, others need exact domain names. Adjust the list accordingly.
Last updated on