Registry #

The Docker Registry is the distribution heart of the Docker ecosystem. Without a registry, images you build on your laptop would never reach production servers, CI pipelines would have no consistent artifact, and team collaboration would stall at “please send me your Docker file by email”. The registry answers one simple question: where are images stored, and how are they distributed to every host that needs them?

This article covers Docker Registries inside and out. We’ll dissect their anatomy (repository, tag, manifest, layer), compare public and private registries, look at the push-pull workflow in detail, and finally discuss the security aspects that are often overlooked: image scanning, supply chain attacks, and safe tagging strategies. After reading this article, you’ll be able to choose the right registry for your organization and design a secure image distribution pipeline.

The Registry’s Role in the Docker Ecosystem #

The registry is the third layer in Docker’s architecture — where images are stored and distributed. Without a registry, the only way to share an image is docker save (exporting to a file) and copying that file to another host. That isn’t scalable, auditable, or secure.

flowchart LR
    DEV[Developer]
    CI[CI/CD Pipeline]
    subgraph REG["Registry"]
        R1[Repository: api]
        R2[Repository: web]
        R3[Repository: worker]
    end
    STAGING[Staging Server]
    PROD_K8S["K8s Cluster<br/>(production)"]
    DEV2[Developer 2]

    DEV -->|"docker push<br/>api:v1.0"| R1
    CI -->|"docker push<br/>api:abc123"| R1
    R1 -->|"docker pull"| STAGING
    R1 -->|"docker pull"| PROD_K8S
    R1 -->|"docker pull<br/>(debug)"| DEV2

The registry is the single source of truth for every image in the organization. An image pushed to the registry can be pulled by anyone with access — from a developer’s laptop to Kubernetes nodes in three different regions. That’s what makes deployments reproducible and scalable.

Why the Registry Matters #

WITHOUT a registry:
  ✗ Images only exist locally on one machine.
  ✗ Deployment requires copying files between hosts (manual, error-prone).
  ✗ No version control for images.
  ✗ No safe way to share images between teams.
  ✗ No audit trail (who pushed what, when).

WITH a registry:
  ✓ Images stored centrally, backed up, replicated.
  ✓ Deployment is just pull by tag.
  ✓ Image versioning via tags and digests.
  ✓ Access control per repository, per user, per team.
  ✓ Complete audit logs (who pushed/pulled what, when).
  ✓ Image scanning for CVEs.
  ✓ CDN-like distribution (images cached at the edge).

A way to remember: think of the registry as the “GitHub for Docker images”. Just as GitHub stores code, the registry stores images. Just as you git clone code, you docker pull images. Just as GitHub has public and private repos, the registry has the same concept.


Registry Anatomy: Repository, Tag, Manifest, Layer #

The registry isn’t just a file server for images. It’s a service managing four core concepts: repository, tag, manifest, and layer. All four are interrelated and form a neat hierarchy.

flowchart TB
    REG["Docker Registry"]
    REG --> REPO_A["Repository: myapp"]
    REG --> REPO_B["Repository: nginx"]
    REG --> REPO_C["Repository: postgres"]

    REPO_A --> TAG_A1["Tag: latest<br/>(mutable)"]
    REPO_A --> TAG_A2["Tag: v1.0.0<br/>(immutable)"]
    REPO_A --> TAG_A3["Tag: v1.1.0<br/>(immutable)"]

    TAG_A2 --> MANIFEST["Image Manifest<br/>(sha256:aaa...)"]
    MANIFEST --> CONFIG["Image Config<br/>(CMD, ENV, etc.)"]
    MANIFEST --> L1["Layer 1<br/>(sha256:111)"]
    MANIFEST --> L2["Layer 2<br/>(sha256:222)"]
    MANIFEST --> L3["Layer 3<br/>(sha256:333)"]

Repository #

A repository is a collection of related images. Usually one repository = one application or one service.

Repository name format:
[NAMESPACE/]REPOSITORY

Examples:
library/nginx                  # official image, namespace "library" implicit
unisbadri/myapi                # user "unisbadri", repo "myapi"
myorg/frontend/web             # org "myorg", sub-namespace "frontend", repo "web"

A repository is the unit of access control. You can grant push permission on a specific repository to one user or team without giving access to other repositories.

Tag #

A tag is an identifier inside a repository. It points to a specific digest of the image manifest.

Full reference format:
[REGISTRY[:PORT]/][NAMESPACE/]REPOSITORY[:TAG][@DIGEST]

Examples:
docker.io/library/nginx:1.25.3
ghcr.io/unisbadri/myapi:v2.1.0
123456789.dkr.ecr.us-east-1.amazonaws.com/prod-api:2026-02-07
myregistry.local:5000/internal/worker@sha256:b5b2b2c5...

Tags are mutable, digests are immutable. A tag is a pointer that can be moved to another manifest (for example on a re-push). A digest is a content-addressable identifier that never changes for the same image.

# Mutable tag: a second push overwrites the pointer
docker push myapp:v1.0       # digest A
docker push myapp:v1.0       # digest B (v1.0 now points to B)

# Immutable digest: pulling by digest always gets the same image
docker pull myapp@sha256:aaa...   # always digest A
Anti-pattern: using the latest tag in production. latest is the default tag that automatically resolves to the NEWEST digest. If you re-push an image with the latest tag, a running container (or a deployment script using latest) will pull the NEW image you haven’t tested. Always use explicit tags (semver, date, commit SHA) or digests for deployments.

Manifest #

A manifest is the JSON describing an image — the list of layers, configuration, and architecture. Manifests are stored in the registry with a content-addressable digest.

{
  "schemaVersion": 2,
  "mediaType": "application/vnd.docker.distribution.manifest.v2+json",
  "config": {
    "mediaType": "application/vnd.docker.container.image.v1+json",
    "digest": "sha256:b5b2b2c507a0944348e0303112d8d08cba2bd51a7f..."
  },
  "layers": [
    {
      "mediaType": "application/vnd.docker.image.rootfs.diff.tar.gzip",
      "digest": "sha256:e692418e4d3c4f7d3e4a5b6c7d8e9f0a1b2c3d4e5f6a7b8c9d0e1f2a3b4c5d6",
      "size": 77956920
    },
    {
      "mediaType": "application/vnd.docker.image.rootfs.diff.tar.gzip",
      "digest": "sha256:3e3b1767b39736f5c0e3b4d5f6a7b8c9d0e1f2a3b4c5d6e7f8a9b0c1d2e3f4a5",
      "size": 12345678
    }
  ]
}

Important fields:

  • config.digest — pointer to the image configuration JSON.
  • layers[] — the list of layers, each with a digest and size.
  • Content-addressable — the digest is the sha256 of the content, so the registry can verify integrity.

Layer #

A layer is an immutable filesystem unit. Each Dockerfile instruction produces one layer. Layers are stored in the registry and cached on hosts that pull them.

flowchart LR
    BASE["Base image<br/>ubuntu:22.04<br/>77 MB"]
    PKG["apt install nginx<br/>12 MB"]
    CONF["COPY config<br/>0.1 MB"]
    BASE --> PKG --> CONF

    CACHE[Host Local Cache]
    BASE -.-> CACHE
    PKG -.-> CACHE
    CONF -.-> CACHE

The benefits of layer sharing:

  • Incremental pulls — if you already have ubuntu:22.04 on the host, pulling a new image using the same base won’t re-download the base layer.
  • Efficient storage — the registry stores one copy of a layer shared by many images.
  • Build cache — when building a new image, the same layers are reused.

Interesting detail: layer sharing happens across repositories, tags, and images. For example, python:3.12-slim and node:20-alpine probably don’t share layers (different bases), but myapp:v1.0 and myapp:v2.0 most likely share many layers (base image, dependencies). That’s why upgrading from v1 to v2 doesn’t always mean “re-download everything”.


Types of Docker Registries #

Registries fall into three broad categories: public registries (anyone can access), private cloud registries (managed by a cloud provider), and self-hosted registries (you run them yourself). Each has different characteristics, advantages, and trade-offs.

Public Registries #

RegistryProviderUse Case
Docker HubDocker Inc.Default, official images, community
GitHub Container Registry (GHCR)GitHubGitHub Actions integration
Quay.ioRed HatImage security, Clair scanning
AWS Public ECR GalleryAWSPublic images from AWS services

Docker Hub is the largest public registry with 100+ billion pulls per year. “Official” images (nginx, redis, postgres, etc.) live in the library/ namespace and are audited by Docker. Community images can be pulled by anyone without authentication.

Advantages:

  • Easy to use, the default in the docker CLI.
  • Official images are quality-assured (regular audits, security updates).
  • Free for public repos.

Drawbacks:

  • Rate limits for anonymous pulls (100 pulls / 6 hours per IP) and authenticated pulls (200 pulls / 6 hours per user).
  • Supply chain risk: unofficial images could be malicious or backdoored.
  • No SLA for availability (usually high, but not guaranteed).

Private Cloud Registries #

ProviderServiceCharacteristics
AWSElastic Container Registry (ECR)IAM integration, auto scan, KMS encryption
Google CloudArtifact RegistryMulti-format (Docker, Maven, npm, Python)
AzureContainer Registry (ACR)AKS integration, geo-replication
DigitalOceanContainer RegistrySimple, clear pricing

Private cloud registries are registries you control (you hold the access) but are managed by the cloud provider. Their main advantages:

  • IAM integration — access control via AWS/GCP/Azure roles, no separate user management.
  • Network proximity — the registry sits in the same region as your cluster, faster pulls.
  • Compliance — the cloud provider’s compliance certifications (SOC2, HIPAA, etc.) apply.
  • Zero operational overhead — no need to install, patch, back up, or scale the registry yourself.

Trade-offs:

  • Vendor lock-in — even though the Docker Registry Protocol is standard, proprietary features (like IAM integration) aren’t portable.
  • Cost — pulls from other regions or over the internet usually incur egress charges.
  • Quotas — some providers limit the number of repos or total size.

Self-Hosted Registries #

Self-hosted registries fit:

  • Very strict compliance needs (on-premise data centers, nothing may leave).
  • Full isolation from the internet.
  • Very low latency between local data centers.
  • Lower long-term costs at high volume.
ToolCharacteristics
Docker Registry (official)registry:2 image, minimal, lightweight
HarborEnterprise-grade, UI, scanning, replication, RBAC
JFrog ArtifactoryUniversal artifact repo (not just containers)
Sonatype NexusUniversal artifact repo, supports many formats
GitLab Container RegistryIntegrated with GitLab CI/CD
flowchart TB
    subgraph DOCKER["Docker Registry (official)"]
        A1[Minimal]
        A2[REST API only]
        A3[No UI]
    end

    subgraph HARBOR["Harbor"]
        B1[Web UI]
        B2[RBAC]
        B3[Vulnerability scan]
        B4[Replication]
        B5[Image signing]
    end

    subgraph ARTIFACTORY["JFrog Artifactory"]
        C1[Universal artifact]
        C2[Build info]
        C3[Enterprise features]
    end

Docker Registry (registry:2) is the minimal implementation — REST API only, no UI, no scanning. Good for small setups or as a building block for larger solutions. Harbor is the most popular enterprise choice: full UI, granular RBAC, image scanning (Trivy/Clair), cross-instance replication, and image signing (Cosign/Notary).

Recommendation: for most teams, start with a cloud provider registry (ECR, GAR, ACR) closest to their infrastructure. Move to self-hosted (Harbor) only when there’s a specific need cloud registries can’t meet: on-premise mandates, special compliance, or volume making cloud registries expensive.


Push and Pull Workflows #

Let’s look in detail at what happens when you docker push and docker pull. This understanding helps debug the push/pull issues that often show up in production.

Push Workflow #

sequenceDiagram
    participant CLI as docker CLI
    participant D as dockerd
    participant REG as Registry

    CLI->>D: docker push myapp:v1.0
    D->>D: Resolve image ID & layer digests
    D->>REG: HEAD /v2/myapp/manifests/v1.0
    REG-->>D: 404 (manifest doesn't exist)
    D->>REG: POST /v2/myapp/blobs/uploads/ (start upload)
    REG-->>D: 202 Accepted (upload UUID)
    D->>REG: PATCH /v2/myapp/blobs/uploads/<uuid> (layer 1)
    REG-->>D: 200 OK
    D->>REG: PATCH /v2/myapp/blobs/uploads/<uuid> (layer 2)
    REG-->>D: 200 OK
    D->>REG: PUT /v2/myapp/blobs/uploads/<uuid>?digest=sha256:... (finalize)
    REG-->>D: 201 Created
    D->>REG: PUT /v2/myapp/manifests/v1.0 (push manifest)
    REG-->>D: 201 Created
    D-->>CLI: push complete

The key steps:

  1. Resolve image & layers — the daemon reads the image ID, then the list of layers that compose it.
  2. Check for an existing manifestHEAD against the registry to see if the manifest already exists. Mounting trick: for layers that already exist, the daemon can POST .../?mount=<digest> to “mount” a layer from another blob without re-uploading.
  3. Upload layers one by one — layers missing from the registry are uploaded via chunked upload (PATCH per chunk).
  4. Push the manifest — once all layers exist, the manifest is pushed and points at those layers.

Pull Workflow #

sequenceDiagram
    participant CLI as docker CLI
    participant D as dockerd
    participant REG as Registry

    CLI->>D: docker pull myapp:v1.0
    D->>REG: GET /v2/myapp/manifests/v1.0
    REG-->>D: manifest (config + layers)
    D->>D: Compare with local cache
    D->>REG: HEAD /v2/myapp/blobs/<layer1-digest>
    REG-->>D: 200 OK (exists)
    D->>REG: GET /v2/myapp/blobs/<layer1-digest>
    REG-->>D: layer 1 data
    D->>REG: HEAD /v2/myapp/blobs/<layer2-digest>
    REG-->>D: 200 OK
    D->>REG: GET /v2/myapp/blobs/<layer2-digest>
    REG-->>D: layer 2 data
    D->>D: Apply layers (extract, snapshot)
    D-->>CLI: pull complete

Optimizations That Happen Frequently #

Layer deduplication and mounting: on push, if a layer with the same digest already exists in the registry (e.g. from another image), the daemon doesn’t re-upload it — it just “mounts” a reference to the existing blob. That’s why pushing a second image with the same base is far faster than the first push.

Parallel downloads: modern pulls (Docker 18.09+) download multiple layers in parallel. An image with many small layers pulls faster than one with a single large layer (relevant for image design).

Resumable uploads: chunked uploads (PATCH) can be resumed if the connection drops. This is a big help for pushing large images over unstable networks.

Host-side caching: after the first pull, layers are cached in /var/lib/docker/overlay2/. Subsequent pulls of images sharing layers won’t re-download them.

A common production problem: Docker Hub rate limits. When CI/CD pulls many different images in a short time, anonymous CI can hit the 100-pull/6-hour rate limit. Solutions: mirror frequently used images to an internal registry, or use authenticated Docker Hub pulls with an organization account (200 pulls/6 hours per user).

Authentication and Authorization #

Modern registries support several authentication mechanisms, from the simplest to the most sophisticated.

Authentication Types #

TypeHow It WorksUse Case
AnonymousNo credentials needed, public registryDocker Hub public images
Basic AuthUsername + password (HTTP Basic)Simple, self-hosted registries
Bearer TokenJWT issued after loginDocker Hub, GHCR, cloud registries
OAuth 2.0Standard OAuth flow, IdP integrationEnterprise, GitHub Apps
IAM-basedCloud IAM (AWS IAM, GCP Service Account)ECR, GAR, ACR
mTLSMutual TLS with client certificatesZero-trust networks

Logging Into a Registry #

# Log in to Docker Hub
docker login
# Username: your-username
# Password: ************

# Log in to a custom registry
docker login registry.example.com

# Log in with IAM (AWS ECR)
aws ecr get-login-password --region us-east-1 | \
  docker login --username AWS --password-stdin 123.dkr.ecr.us-east-1.amazonaws.com

# Log out
docker logout registry.example.com

Credentials are stored in ~/.docker/config.json. For CI/CD environments, it’s safer to use short-lived tokens or a secret manager than hardcoded passwords.

Authorization — Access Control #

Authorization is who is allowed to do what — usually more complex than authentication.

ActionExample
PullWho may pull images from repo X?
PushWho may push images to repo Y?
DeleteWho may delete images or tags?
ListWho may view the image list in a registry?

Authorization implementations differ per registry:

  • Docker Hub: simple roles (admin, contributor, reader) per repo.
  • AWS ECR: IAM policies with Action: ecr:GetDownloadUrlForLayer etc.
  • Harbor: granular roles (project admin, developer, guest) per project.
  • GHCR: package visibility (public, private, internal) + GitHub team permissions.

Least privilege principle: for CI/CD, create a dedicated service account that only has push permission to the registry, and rotate its token regularly. Don’t use personal developer accounts to push from CI — if a token leaks, a service account scoped to specific repos has a smaller blast radius.


Image Scanning and Supply Chain Security #

A registry isn’t just a place to store images — it’s also an important control point for security. Pushed images can contain vulnerabilities (CVEs in packages) or even malicious code (backdoors, crypto miners). Image scanning detects these before images reach production.

Image Scanning Anatomy #

flowchart LR
    A[Image pushed] --> B[Scanner]
    B --> C{Scan type}
    C -- Vulnerability --> D[Trivy, Snyk, Clair]
    C -- Compliance --> E[Config scan, CIS benchmark]
    C -- Secrets --> F[GitLeaks, TruffleHog]
    D --> G[Report CVEs]
    E --> H[Report config issues]
    F --> I[Report leaked secrets]
    G --> J{Gate}
    H --> J
    I --> J
    J -- Critical --> K[Block deploy]
    J -- High --> L[Manual review]
    J -- Medium/Low --> M[Deploy with monitoring]
ToolTypeLicenseIntegration
TrivyVulnerability, misconfig, secretsOpen source (Apache 2.0)CI/CD, registry, runtime
SnykVulnerability, licenseCommercial + free tierGitHub, GitLab, registry
ClairVulnerability (static analysis)Open source (Apache 2.0)Quay, Harbor
Docker ScoutVulnerability, recommendationsCommercial (Docker subscription)Docker Hub, Docker Desktop
AWS ECR ScanVulnerabilityIncluded with ECRAWS ecosystem
GCP Container AnalysisVulnerabilityIncluded with GARGCP ecosystem
AnchoreVulnerability, policyOpen source + commercialJenkins, K8s

Supply Chain Attacks — a Real Risk #

A supply chain attack is one where attackers infiltrate a dependency or tool you use, so malicious code reaches the images you build. Some real examples:

  • SolarWinds (2020) — malicious code in software updates used by many companies.
  • CodeCov (2021) — credentials leaked from a CI tool; attackers modified the scripts being pulled.
  • event-stream npm (2018) — the original maintainer handed the package to an attacker; malicious code reached thousands of projects.
  • XZ Utils backdoor (2024) — malicious code in a compression library, nearly reaching many Linux distributions.

Layered defense for supply chain security:

flowchart TB
    A[Base image] --> B{Pinned to digest?}
    B -- Yes --> C[Verify integrity]
    B -- No --> D[Auto-update could break]
    C --> E[Build image]
    E --> F{Scan?}
    F -- Yes --> G[Detect CVEs]
    F -- No --> H[Blind deploy]
    G --> I{Sign image?}
    I -- Yes --> J[Verify signature]
    I -- No --> K[Anyone could replace]
    J --> L[Deploy]
  1. Pin base images to digestsFROM ubuntu@sha256:abc... instead of FROM ubuntu:22.04. This prevents mutable tags.
  2. Verify image signatures — use Cosign (part of Sigstore) or Docker Content Trust (DCT) to sign images.
  3. Scan at every stage — don’t just scan the final image; scan the base image and intermediate layers too.
  4. Use trusted base imagesdocker.io/library/* (official) or images from trusted vendors.
  5. Allowlisting — in K8s, use admission controllers (OPA, Kyverno) that only permit images from allowed registries.
Anti-pattern: pulling base images without verification. If an attacker compromises Docker Hub and swaps the library/nginx image for a backdoored one, every build that doesn’t pin to a digest automatically becomes compromised. Always pin base images to SHA256 digests and verify their signatures.

Self-Hosted Docker Registry — a Practical Setup #

For a simple setup, the official Docker Registry (registry:2) is enough. Here’s an example docker-compose.yml for a self-hosted registry with TLS and basic auth.

# docker-compose.yml
version: "3.9"

services:
  registry:
    image: registry:2
    restart: always
    ports:
      - "5000:5000"
    environment:
      REGISTRY_HTTP_SECRET: a-very-long-random-string
      REGISTRY_AUTH: htpasswd
      REGISTRY_AUTH_HTPASSWD_PATH: /auth/htpasswd
      REGISTRY_AUTH_HTPASSWD_REALM: Registry Realm
      REGISTRY_STORAGE_FILESYSTEM_ROOTDIRECTORY: /var/lib/registry
    volumes:
      - ./auth:/auth
      - registry-data:/var/lib/registry
    deploy:
      resources:
        limits:
          memory: 512M

volumes:
  registry-data:
# Create an admin user
mkdir -p auth
docker run --rm \
  --entrypoint htpasswd \
  httpd:2.4 -bn admin secretpassword > auth/htpasswd

# Run it
docker compose up -d

# Log in and push
docker login registry.local:5000
docker tag myapp:v1.0 registry.local:5000/myapp:v1.0
docker push registry.local:5000/myapp:v1.0

For an enterprise setup, Harbor is a more solid choice. Harbor adds:

  • A web UI for browsing images and managing users.
  • Image scanning (built-in Trivy/Clair).
  • Replication between Harbor instances (multi-region).
  • Image signing and verification (Cosign, Notary).
  • Granular per-project RBAC.

Registry Best Practices for Production #

Safe Tagging Strategies #

# DON'T in production
docker push myapp:latest       # mutable, not reproducible

# DO in production
docker push myapp:v1.4.2       # semantic version
docker push myapp:v1.4.2-alpine  # base variant
docker push myapp:1.4.2-build.123  # build number
docker push myapp:2026-02-07-abc1234  # date + short SHA

# BEST for reproducibility
docker push myapp@sha256:b5b2b2c5...  # full digest

Separate Registries per Environment #

flowchart TB
    DEV[Development] --> R1[registry.dev.internal]
    STAGING[Staging] --> R2[registry.staging.internal]
    PROD[Production] --> R3[registry.prod.internal]
    CI[CI/CD Pipeline] --> R1
    CI --> R2
    CI --> R3

Separating registries per environment prevents untested images from leaking into production and makes auditing easier (who pushed to the production registry).

Enable Image Scanning #

Make sure every image entering the registry is scanned, and deployment is blocked for images with critical CVEs.

Retention Policies #

A registry that’s never cleaned will bloat. Implement a retention policy:

  • Keep the last N tags per repository.
  • Keep tags from the last X days.
  • Delete untagged images older than Y days.
  • Archive old images to object storage (S3, GCS) before deletion.

Clean Up Old Images #

# Remove images with no tags that are more than 30 days old
docker exec registry /bin/registry garbage-collect \
  -m /etc/docker/registry/config.yml

# Or in Harbor: Settings > Tag Retention > create an automatic policy

General best practice: set retention policies per environment — dev/staging can be more aggressive (delete after 7 days), production more conservative (keep 90 days or per compliance). For disaster recovery, always back up the production registry to a second region.


Decision Tree — Choosing the Right Registry #

flowchart TD
    A{Deployment<br/>environment?}
    A -- Public internet --> B{Budget?}
    B -- Minimal --> C[Docker Hub free]
    B -- Available --> D[Docker Hub Pro / Team]
    A -- Cloud AWS --> E[ECR]
    A -- Cloud GCP --> F[Artifact Registry]
    A -- Cloud Azure --> G[Container Registry]
    A -- On-premise --> H{Need enterprise<br/>features?}
    H -- Yes --> I[Harbor]
    H -- Minimal --> J[Docker Registry 2.x]
    A -- Hybrid --> K[Harbor replicated<br/>with cloud registry]

    E --> L[Enable scan, KMS]
    F --> M[Enable scan, CMEK]
    G --> N[Enable scan, geo-replication]
    I --> O[Set up RBAC, scan, signing]
    J --> P[TLS, basic auth, backup]

Choosing a registry isn’t a one-time decision. Many organizations start with a cloud registry (ECR/GAR) for setup speed, then add Harbor for specific images needing more control (e.g. proprietary images with sensitive IP). Hybrid patterns like this are common in large companies.


Summary #

  • The Docker Registry is the single source of truth for images in an organization. It answers “where are images stored” and “how are images distributed to every host” — without it, images can only be shared manually via files.
  • The four core registry concepts: repository (a collection of related images), tag (a mutable label for identification), manifest (the JSON describing an image), layer (an immutable filesystem unit). Tags are mutable, digests immutable — for reproducibility, always pull by digest.
  • Three registry types: public (Docker Hub, GHCR) for open-source and community images; private cloud (ECR, GAR, ACR) for proprietary images with IAM integration; self-hosted (Harbor) for full control and strict compliance.
  • The push workflow uploads layers one by one (chunked), with a mounting trick for layers that already exist. The pull workflow downloads the manifest first, then only the layers missing from the cache. Layer sharing makes pulls incremental and storage efficient.
  • Image scanning is a mandatory gate in production pipelines. Trivy, Snyk, and Docker Scout detect CVEs in packages. Supply chain security requires layered defense: pin base images to digests, sign images (Cosign), scan at every stage, and allowlist images in the K8s admission controller.
  • Safe tag strategy: use semantic versions, commit SHAs, or digests. NEVER use latest in production. Separate registries per environment, enable image scanning, and set retention policies to prevent registry bloat.
  • Docker Registry 2.x is the minimal self-hosted choice. Harbor is the enterprise choice with UI, RBAC, scanning, and replication. Choose based on control needs, compliance, and operational overhead.
  • Image signing (Cosign, Notary) and admission controllers (OPA, Kyverno) in K8s provide the final line of defense: only signed images from allowed registries can be deployed.

← Previous: Container   Next: How It Works →

About | Author | Content Scope | Editorial Policy | Privacy Policy | Disclaimer | Contact