“Why did it pull the image when I only typed nginx?”
β This single question, often the first one beginners ask,
contains the entire core philosophy of Docker registries.
>
π― What This Article Covers
- The full format and omission rules for Docker image references
- The 3-step mechanism by which nginx expands to docker.io/library/nginx:latest
- Whether the default registry can be changed in a vanilla Docker Engine (Spoiler: No)
- Practical methods to bypass this using registry-mirrors and Podman’s unqualified-search-registries
- Security threats posed by short names β typosquatting
π Why This Topic Is Important
You type docker pull nginx dozens of times a day. It’s so natural that you’ve probably never questioned it. However, this single line is actually the result of three automatic corrections. If you don’t know these correction rules, you’ll immediately get stuck in situations like these:
- When you need to centralize image sources to an internal private registry
- When you need to deploy in environments where Docker Hub access is restricted, such as China or the Middle East
- When you need to circumvent Docker Hub’s pull rate limit (100 pulls per hour for anonymous users)
- When you need to defend against Typosquatting attacks (malicious images with similar names)
In other words, this mechanism is not just a “convenience feature,” but a critical design point intertwined with operations, security, and cost.

π The Complete Format of Image References
A Docker image reference follows this structure:
[REGISTRY[:PORT]/]NAMESPACE/REPOSITORY[:TAG|@DIGEST]
| Component | Description | Default if omitted |
| REGISTRY | Registry domain where the image is stored | docker.io |
| NAMESPACE | Organization, user, or special namespace | library (docker.io specific) |
| REPOSITORY | Image repository name | (Cannot be omitted) |
| TAG | Version or variant identifier | latest |
So, when the Docker daemon internally processes a single line like docker pull nginx, three stages of automatic correction occur sequentially as follows:
nginx
β (1) λ μ§μ€νΈλ¦¬ μλ΅ β docker.io λΆμ
docker.io/nginx
β (2) λ€μμ€νμ΄μ€ μλ΅ β library λΆμ
docker.io/library/nginx
β (3) νκ·Έ μλ΅ β latest λΆμ
docker.io/library/nginx:latest
If you actually run docker pull nginx, you’ll see docker.io/library/nginx:latest printed on the last line. Docker is kindly telling you, “This is how I actually interpreted it.”
The Identity of the ‘library’ Namespace
There’s a common misconception here. The library namespace is a special rule specific to the docker.io registry, not a universal convention. This means that this rule does not apply to other registries like Quay.io or GCR.
# Automatic 'library' correction only happens on docker.io
docker pull nginx # β
docker.io/library/nginx:latest
docker pull quay.io/nginx # β Does not change to quay.io/library/nginx
docker pull quay.io/bitnami/nginx # β
Namespace must be explicitly specified
Therefore, when pulling images from registries other than docker.io, you must use the fully-qualified image name (FQIN) including the namespace.
π§ Can the Default Registry Be Changed?
To summarize, here’s the conclusion:
Runtime Default Registry Change Notes
| Runtime Default Registry | Change | Notes |
| Docker Engine (Vanilla) | β Impossible | Hardcoded to docker.io |
| Docker Engine + registry-mirrors | πΆ Bypass only | Pull-through mirror for docker.io |
| Podman / CRI-O / Buildah | β Possible | unqualified-search-registries |
| RHEL-based patched Docker | β Possible | ADD_REGISTRY, BLOCK_REGISTRY options (legacy) |
1) Vanilla Docker Engine β The Default Registry Cannot Be Changed
Upstream Docker does not provide an option to change the default registry to another location. Docker Hub always remains the final search destination. This is an intentional design choice, a conservative approach to reduce name conflicts and attack surface.
2) registry-mirrors β A Method of “Inserting in Front” Rather Than “Changing”
Instead, Docker allows you to register a pull-through cache mirror for Docker Hub. You can modify /etc/docker/daemon.json as follows:
{
"registry-mirrors": [
"https://mirror.gcr.io",
"https://registry.mycompany.internal"
]
}
Then restart the daemon.
sudo systemctl restart docker
The operating principle of this setting is as follows:
- When a
docker pull nginxrequest comes in, - The Docker daemon first checks the registered mirrors in order,
- If the image is found in a mirror, it pulls from there,
- If not, it ultimately goes to docker.io.
In other words, the default is “inserted in front” rather than “replaced.” The fact that Docker Hub is the ultimate backend does not change. This pattern is widely used in regions like Korea and China where Docker Hub access is slow or unstable.
3) Podman β The Default Registry Can Truly Be Changed
On the other hand, Podman, Buildah, and CRI-O families can be fully configured using /etc/containers/registries.conf.
# /etc/containers/registries.conf
# List of registries to search for short names (e.g., nginx) β searched in order
unqualified-search-registries = [
"registry.mycompany.internal",
"quay.io",
"docker.io"
]
# short-name resolution mode
# If "enforcing", forces user to choose when ambiguous
short-name-mode = "enforcing"
# Attach a mirror to docker.io
[[registry]]
prefix = "docker.io"
location = "docker.io"
[[registry.mirror]]
location = "mirror.mycompany.internal:5000"
After configuring this, if you run podman pull nginx, it will search registry.mycompany.internal first, and then sequentially. This is much more flexible than Docker. This difference is one of several reasons why the RHEL/OpenShift ecosystem adopted Podman instead of Docker as its default.
β οΈ Security Threat Posed by Short Names β Typosquatting
Being able to change the default registry is convenient, but it’s also a trap. If you register multiple registries in unqualified-search-registries, it becomes ambiguous which registry an image came from.
For example, if the search order is ["registry1.com", "registry2.com"], and an attacker uploads a malicious image named nginx to registry1 first, even if the legitimate nginx is on registry2, the malicious image from registry1 will be pulled first. This is a short-name squatting attack.
3 Principles of Defense
- Use FQIN: In production, always specify the registry, namespace, and tag, like
docker.io/library/nginx:1.27.3. - Pin Digests: More strictly, pin by hash like
nginx@sha256:72297.... Ensures the exact same image. - Short-name-mode = “enforcing” (Podman only): Configure to prompt the user for confirmation when a short name is ambiguous.
β Summary
The seemingly ordinary line docker pull nginx is actually the result of three automatic corrections β registry β namespace β tag β expanding to docker.io/library/nginx:latest. This rule is a convention specific to Docker Hub, not a universal syntax in the container world.
The core points can be summarized as follows:
- In a vanilla Docker Engine, the default registry cannot be changed β you can only insert a cache in front using
registry-mirrors. - Podman-based systems can be fully configured with
unqualified-search-registries. - Convenience comes at the cost of ambiguity β in production, always make it a habit to use FQIN or digest pinning.
If you’re interested in the next steps, you might want to learn about Docker Hub pull rate limit evasion strategies, building private registries like Harbor/ECR, Sigstore-based image signature verification (cosign), and automating digest pinning in CI/CD pipelines.

Leave a Reply