Skip to content

Docker Registry

  • A Docker Registry is a service that stores and distributes Docker images. It’s the central repository from which images are pulled and to which they are pushed.

  • Images are identified as [registry-host/][namespace/]name[:tag]

  • Default registry: Docker Hub (docker.io). If no registry is specified in an image name, Docker Hub is assumed.

  • Images can reach a Docker host through four sources: pulled from Docker Hub, pulled from a private registry, loaded from an offline TAR archive, or built locally from a Dockerfile.

    The four ways to get a Docker image onto a host
Image distribution spectrum
MethodPrimary Use CaseAdvantageKey Weakness
Docker HubDefault public open-source softwareZero configurationThird-party reliance - not suitable for proprietary code
Private RegistriesCorporate infrastructureComplete access control and central governanceIntroduces a single point of failure
Tar ArchivesAir-gapped or offline networksBypasses network requirements completelyNo distribution facilities or version tracking
DockerfilesVersion-controlled team projectsExtremely lightweight to shareRisk of dependency drift during delayed builds

When no single method is universally optimal, evaluate your requirements against these nine criteria:

CriterionWhat It Determines
CostFree (public hosted) to expensive (self-hosted infra). Typically the most flexible criterion — organizations trade cost for features like confidentiality.
VisibilityHow discoverable your images are. Open-source wants maximum; internal tools and proprietary software need minimum.
Transport speedConcurrent layer downloads vs. flat file shipping. Critical for just-in-time deployments; less important for local dev.
Access controlProtection from unauthorized access. Rigid — typically dictated by compliance or business requirements.
Artifact integrityCryptographic guarantees that what you pull is what was published. Protects against man-in-the-middle and registry compromise.
Artifact confidentialityWhether image contents are protected from the provider itself. Hosted registries can never fully satisfy the most sensitive requirements.
Longevity controlBusiness risk of relying on third parties. If a hosted provider shuts down, you lose your distribution channel.
Availability controlYour ability to resolve outages directly. Hosted = SLA only. Self-hosted = full control and full responsibility.
Requisite expertiseHosted tools need minimal knowledge. Private registries and custom pipelines require deep operational experience.
RegistryProviderTypeNotes
Docker HubDocker IncPublic/PrivateDefault. Free tier has pull rate limits.
GitHub Container Registry (GHCR)GitHubPublic/PrivateIntegrated with GitHub Actions.
Azure Container Registry (ACR)MicrosoftPrivateNative AKS/Azure integration. Geo-replication.
AWS ECRAmazonPrivateNative ECS/EKS integration. IAM auth.
Google Artifact RegistryGooglePrivateReplaced GCR. Multi-format (Docker, npm, Maven).
HarborCNCFSelf-hostedOpen-source. Vulnerability scanning, RBAC.
Nexus RepositorySonatypeSelf-hostedMulti-format proxy and private registry.
Terminal window
# Login to Docker Hub
docker login
# ⚠️ Credentials are stored as base64 in ~/.docker/config.json — not encrypted.
# Use a credential helper (docker-credential-pass, docker-credential-osxkeychain)
# to store them securely. See: docker help login
# Login to a private registry
docker login myregistry.azurecr.io
# Pull an image from Docker Hub (implicit registry)
docker pull nginx:alpine
# Pull from a private registry (must be logged in)
docker pull myregistry.azurecr.io/apps/my-app:v1.2
# Tag an existing local image for a registry
docker tag my-app:latest myregistry.azurecr.io/team/my-app:v1.2
# Push to registry
docker push myregistry.azurecr.io/team/my-app:v1.2
# List images in a registry (Docker Hub example via API)
curl -s https://hub.docker.com/v2/repositories/library/nginx/tags | jq '.results[].name'
# Logout
docker logout myregistry.azurecr.io

Docker distributes an official registry image you can self-host:

Terminal window
# Run a local registry on port 5000
docker run -d \
-p 5000:5000 \
--name registry \
--restart=always \
-v registry-data:/var/lib/registry \ # persist images
registry:2
# Push an image to your local registry
docker tag my-app:latest localhost:5000/my-app:latest
docker push localhost:5000/my-app:latest
# Pull from your local registry
docker pull localhost:5000/my-app:latest

For a production private registry, add:

  • TLS: Use a reverse proxy (Nginx, Caddy) with a valid certificate. Docker requires HTTPS for non-localhost registries.
  • Authentication: Basic auth via htpasswd file, or proxy through an OAuth provider.
  • Vulnerability scanning: Use Harbor or Trivy integration.

A self-hosted registry enables distribution architectures that hosted services cannot:

  • Regional caches — Deploy registry instances per geographic region to reduce pull latency for distributed teams.
  • Environment-specific pools — Separate registries for dev, staging, and production enforce promotion workflows and prevent accidental cross-environment image usage.
  • Corporate approval gates — Images must pass vulnerability scanning and policy checks before being promoted to a production registry.
  • Team-scoped namespaces — Restrict image visibility and write access at the team level, invisible to the broader organization.
  • Mirroring external dependencies — Copy images from Docker Hub into your own registry to insulate against upstream deletions, tag changes, or rate limits.
StrengthConsideration
First method truly suitable for trade secrets — you control TLS, storage encryption, and authRequires substantial operational expertise to run at high availability
No external network dependency — clients pull from internal infrastructureCost scales with storage volume and transaction rate, not a flat per-repo fee
100% longevity and availability controlThe open-source Distribution server ships with no auth — you must integrate NGINX, LDAP/Kerberos, and Redis separately
Image name anatomy
Image ReferenceResolved To
nginxdocker.io/library/nginx:latest
nginx:alpinedocker.io/library/nginx:alpine
myuser/myappdocker.io/myuser/myapp:latest
myregistry.io/myapp:v2myregistry.io/myapp:v2
myapp@sha256:abc123Content-addressed, exact digest
  • Digest pinning: sha256:... digests are immutable. Unlike tags, they cannot be overwritten. Use digests in production for reproducible deployments.

  • Tags are mutable. The image behind nginx:latest changes every time a new release is published. Never rely on latest in CI/CD.

  • Content Trust: Set DOCKER_CONTENT_TRUST=1 to enforce that only cryptographically signed images are pulled. Requires the image publisher to sign with Docker Notary. Not widely adopted outside official images, but useful in high-security environments.

    Tag types
  • Unauthenticated pulls: 100 pulls per 6 hours per IP.
  • Authenticated free tier: 200 pulls per 6 hours per account.
  • Paid plans: unlimited.

For CI/CD pipelines that frequently pull from Docker Hub, either:

  1. Authenticate your CI runner with a Docker Hub account.
  2. Mirror Docker Hub through a private registry (pull-through cache) to avoid rate limits hitting your entire team.
Terminal window
# Configure a Docker Hub pull-through mirror in /etc/docker/daemon.json
cat > /etc/docker/daemon.json <<'EOF'
{
"registry-mirrors": ["https://my-dockerhub-mirror.mycompany.io"]
}
EOF
# Restart Docker for the change to take effect
systemctl restart docker

When a registry is unavailable - air-gapped environments, offline CI, or transferring images between isolated networks - Docker images can be packaged into a TAR archive and loaded on another machine:

Terminal window
# Export one or more images to a TAR archive
docker save -o my-images.tar nginx:alpine my-app:v1.2
# Verify the archive contents
tar -tf my-images.tar
# Transfer the file (scp, USB, shared volume, etc.)
scp my-images.tar user@target-host:/tmp/
# Load images from the archive on the target machine
docker load -i /tmp/my-images.tar
# Confirm images are available
docker images

Instead of distributing a built image, you distribute the Dockerfile and build context and let consumers build the image themselves. Standard registry commands (docker push, docker pull) are not involved — the only Docker tool used is the builder.

This is the most flexible method on the distribution spectrum since any version-control or file-sharing tool works, but it places the full build burden on the consumer.

  • Producer: Packages the Dockerfile and required source files into a repository. No registry account required.
  • Consumer: Clones the repository and runs docker build locally. Must be familiar with Docker build tooling and Dockerfile syntax.
Terminal window
# Producer — publish the source
git init && git add Dockerfile src/
git commit -m "chore: initial Dockerfile"
git remote add origin https://github.com/org/my-app
git push origin main
# Consumer — build from source
git clone https://github.com/org/my-app
docker build -t my-app:local ./my-app
CriterionRatingNotes
Cost✅ BestFree on public GitHub repositories
Visibility✅ BestGitHub search and social discovery
Transport speed✅ GoodGitHub CDN + consumer pulls base layers from the nearest registry
Access control✅ GoodPrivate repos with collaborator management
Artifact integrity✅ GoodGit history makes tampering visible and recoverable
Requisite expertise⚠️ GoodRequires Git + Dockerfile familiarity from both parties
Longevity & availability❌ WorstEntirely dependent on GitHub’s uptime and business continuity
Confidentiality❌ WorstSource code is fully exposed on public repositories