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.