How to Configure Windows Server 2019 Container Registry

A container registry is a repository for storing and distributing container images. In Windows Server 2019 environments, you can deploy an on-premises container registry using the open-source Docker Registry v2 or a Windows-compatible private registry, or integrate with Azure Container Registry. A private registry gives you full control over your images, reduces dependency on external services, and speeds up image pulls within your datacenter. This guide covers deploying and configuring a private container registry on Windows Server 2019.

Understanding Registry Options

Several registry options are available for Windows Server 2019. The Docker Distribution Registry (registry:2) is a lightweight open-source registry that can run as a container or as a Windows service. Azure Container Registry (ACR) is a managed cloud service that integrates with Azure and supports geo-replication. Harbor is an enterprise-grade registry with RBAC, vulnerability scanning, and replication. For most on-premises Windows Server 2019 deployments, Docker Distribution Registry is the most straightforward option.

Deploying Docker Registry as a Container

The Docker Registry server runs as a Linux container and can be hosted on a Windows Server 2019 host running Linux containers mode, or on a separate Linux host accessible from Windows nodes. However, the Windows Docker clients connect to it regardless of where it runs. First, create a directory for registry storage on the host:

New-Item -ItemType Directory -Path C:RegistryData -Force
New-Item -ItemType Directory -Path C:RegistryCerts -Force

Generate a self-signed TLS certificate for the registry (or use a CA-signed certificate in production):

$cert = New-SelfSignedCertificate `
    -DnsName "registry.contoso.local" `
    -CertStoreLocation "Cert:LocalMachineMy" `
    -KeyAlgorithm RSA `
    -KeyLength 4096 `
    -NotAfter (Get-Date).AddYears(5) `
    -FriendlyName "Container Registry TLS"

Export-Certificate -Cert $cert -FilePath "C:RegistryCertsregistry.cer"

$pwd = ConvertTo-SecureString -String "RegistryExportPass!" -AsPlainText -Force
Export-PfxCertificate -Cert $cert -FilePath "C:RegistryCertsregistry.pfx" -Password $pwd

Installing Registry as a Windows Service

For a Windows-native installation, deploy the registry binary directly. Download the registry binary and create a Windows service:

$registryVersion = "2.8.3"
Invoke-WebRequest -UseBasicParsing `
    -Uri "https://github.com/distribution/distribution/releases/download/v$registryVersion/registry_${registryVersion}_windows_amd64.zip" `
    -OutFile "C:registry.zip"
Expand-Archive -Path "C:registry.zip" -DestinationPath "C:Registry" -Force

Create the registry configuration file at C:Registryconfig.yml:

version: 0.1
log:
  fields:
    service: registry
storage:
  cache:
    blobdescriptor: inmemory
  filesystem:
    rootdirectory: C:/RegistryData
http:
  addr: :5000
  tls:
    certificate: C:/RegistryCerts/registry.pem
    key: C:/RegistryCerts/registry-key.pem
  headers:
    X-Content-Type-Options: [nosniff]
health:
  storagedriver:
    enabled: true
    interval: 10s
    threshold: 3

Install and start the registry as a Windows service using NSSM or sc.exe:

sc.exe create RegistryService `
    binPath= "C:Registryregistry.exe serve C:Registryconfig.yml" `
    start= auto `
    DisplayName= "Docker Container Registry"

sc.exe description RegistryService "Private Docker Container Registry v2"
sc.exe start RegistryService

Configuring Authentication for the Registry

Enable htpasswd-based authentication to secure the registry. Generate a password hash file using the htpasswd utility (available in Git for Windows or WSL):

htpasswd -cB C:Registryauthhtpasswd registry_admin

Update config.yml to enable authentication:

auth:
  htpasswd:
    realm: "Registry Realm"
    path: C:/Registry/auth/htpasswd

Trusting the Registry Certificate on Client Machines

Each Docker client host must trust the registry’s TLS certificate. Copy the registry certificate to the Docker certificates directory:

$registryHost = "registry.contoso.local:5000"
New-Item -ItemType Directory -Path "C:ProgramDatadockercerts.d$registryHost" -Force
Copy-Item "C:RegistryCertsregistry.cer" "C:ProgramDatadockercerts.d$registryHostca.crt"
Restart-Service docker

Also import the certificate into the Windows trusted root store on each client:

Import-Certificate -FilePath "C:RegistryCertsregistry.cer" -CertStoreLocation "Cert:LocalMachineRoot"

Pushing and Pulling Images

Log into the private registry from the Docker client:

docker login registry.contoso.local:5000

Tag an existing image for the private registry and push it:

docker tag mcr.microsoft.com/windows/servercore:ltsc2019 registry.contoso.local:5000/windows/servercore:ltsc2019
docker push registry.contoso.local:5000/windows/servercore:ltsc2019

Pull an image from the private registry on another host:

docker pull registry.contoso.local:5000/windows/servercore:ltsc2019

List all repositories in the registry using the API:

Invoke-RestMethod -Uri "https://registry.contoso.local:5000/v2/_catalog" -Credential (Get-Credential)

Configuring Image Replication

Configure the registry to act as a pull-through cache for the Microsoft Container Registry, reducing external bandwidth for frequently used Windows images:

proxy:
  remoteurl: https://mcr.microsoft.com
  username: ""
  password: ""

Registry Garbage Collection

Over time, deleted image layers remain on disk until garbage collection is run. Schedule garbage collection to reclaim disk space:

C:Registryregistry.exe garbage-collect C:Registryconfig.yml --delete-untagged=true

Add this command as a scheduled task to run during maintenance windows:

$action = New-ScheduledTaskAction -Execute "C:Registryregistry.exe" -Argument "garbage-collect C:Registryconfig.yml --delete-untagged=true"
$trigger = New-ScheduledTaskTrigger -Weekly -DaysOfWeek Sunday -At "02:00"
Register-ScheduledTask -TaskName "RegistryGC" -Action $action -Trigger $trigger -RunLevel Highest

A private container registry on Windows Server 2019 is an essential component of any production container environment. It reduces reliance on external registries, improves security by keeping proprietary images in-house, and reduces image pull times for frequently used images. Pair it with a firewall rule restricting access to the registry port (5000) from only authorized hosts for maximum security.