How to Set Up a Private Container Registry on Windows Server 2025

Running a private container registry gives your team full control over image storage, distribution, and access without depending on Docker Hub or a cloud registry. On Windows Server 2025, the most practical approach is to deploy the open-source Docker Registry v2 image as a Linux container, accessed from your Windows Docker hosts, or to deploy Harbor on a Linux host and configure your Windows Server 2025 Docker daemon to use it. This tutorial covers both approaches, explains the important architectural distinction between Windows and Linux containers, and shows you how to configure Docker on Windows Server 2025 to push and pull images securely from a private registry.

Prerequisites

  • Windows Server 2025 with the Containers feature installed.
  • Docker Engine installed on Windows Server 2025 (via the DockerMsftProvider or the official Docker install script).
  • A Linux host (Ubuntu 22.04 or later recommended) if deploying Harbor or the registry container in Linux mode.
  • A DNS name or static IP for the registry host (e.g., registry.corp.local).
  • PowerShell or Command Prompt running as Administrator.
  • Optional: a TLS certificate for the registry FQDN (strongly recommended for production).

Step 1: Understand the Windows vs Linux Container Distinction

The official registry:2 image from Docker Hub is a Linux container image. Windows Server 2025 with Docker Engine can run Linux containers using the Linux Containers on Windows (LCOW) feature, or you can point your Windows Docker daemon at a registry running on a separate Linux host. For production environments, running the registry on a dedicated Linux VM and configuring Windows Server 2025 as a client is the cleaner and more supportable architecture. Attempting to run the registry image natively as a Windows container is not supported — there is no Windows-native registry:2 image.

Step 2: Deploy Docker Registry v2 on a Linux Host

On your Linux registry host, install Docker and run the official registry container. These commands are run on the Linux host, not on Windows Server 2025.

# On the Linux registry host
sudo apt-get update && sudo apt-get install -y docker.io

# Create a directory to persist registry data
sudo mkdir -p /opt/registry/data

# Run the registry container
sudo docker run -d 
  --name private-registry 
  --restart=always 
  -p 5000:5000 
  -v /opt/registry/data:/var/lib/registry 
  registry:2

# Verify it is running
sudo docker ps | grep registry

The registry is now listening on port 5000 of the Linux host. Note the IP address or hostname of this server — you will need it when configuring Windows Server 2025.

Step 3: Configure TLS for the Registry (Recommended)

Docker clients refuse to communicate with an HTTP registry by default. Either provide a TLS certificate or explicitly mark the registry as insecure. For a production setup, generate or obtain a certificate for your registry hostname and configure the registry to use it.

# On the Linux registry host — place your certificate files
sudo mkdir -p /opt/registry/certs
sudo cp registry.corp.local.crt /opt/registry/certs/
sudo cp registry.corp.local.key /opt/registry/certs/

# Restart registry with TLS enabled
sudo docker rm -f private-registry
sudo docker run -d 
  --name private-registry 
  --restart=always 
  -p 443:5000 
  -v /opt/registry/data:/var/lib/registry 
  -v /opt/registry/certs:/certs 
  -e REGISTRY_HTTP_TLS_CERTIFICATE=/certs/registry.corp.local.crt 
  -e REGISTRY_HTTP_TLS_KEY=/certs/registry.corp.local.key 
  registry:2

Step 4: Configure Windows Server 2025 to Use the Private Registry (Insecure Mode)

If you are using HTTP or a self-signed certificate for testing, configure the Docker daemon on Windows Server 2025 to trust the registry as an insecure source. The Docker daemon configuration file on Windows is located at C:ProgramDataDockerconfigdaemon.json.

# On Windows Server 2025
$daemonConfigPath = "C:ProgramDataDockerconfigdaemon.json"

# Create or update daemon.json
$config = @{
    "insecure-registries" = @("registry.corp.local:5000")
    "log-driver" = "json-file"
    "log-opts" = @{
        "max-size" = "10m"
        "max-file" = "3"
    }
} | ConvertTo-Json -Depth 5

# Write config (creates file if it does not exist)
$config | Set-Content -Path $daemonConfigPath -Encoding UTF8

# Restart Docker to apply changes
Restart-Service docker

# Verify Docker is running
Get-Service docker

Step 5: Authenticate with the Private Registry

If your registry requires authentication (configured via htpasswd on the registry container), log in using docker login from Windows Server 2025.

# Log in to the private registry from Windows Server 2025
docker login registry.corp.local:5000

# For non-interactive (CI/CD) use, pass credentials directly
# WARNING: Avoid storing plain-text passwords in scripts; use a secrets manager
$RegistryUser = "registryuser"
$RegistryPass = Read-Host -Prompt "Registry password" -AsSecureString
$PlainPass = [Runtime.InteropServices.Marshal]::PtrToStringAuto(
    [Runtime.InteropServices.Marshal]::SecureStringToBSTR($RegistryPass)
)
docker login registry.corp.local:5000 -u $RegistryUser -p $PlainPass
[Runtime.InteropServices.Marshal]::ZeroFreeBSTR(
    [Runtime.InteropServices.Marshal]::SecureStringToBSTR($RegistryPass)
)

Step 6: Push and Pull Images to the Private Registry

Tag a local image with the registry address as a prefix, then push it. Pull it back on any configured Docker host.

# Pull a base Windows Server Core image (Windows container)
docker pull mcr.microsoft.com/windows/servercore:ltsc2025

# Tag it for your private registry
docker tag mcr.microsoft.com/windows/servercore:ltsc2025 `
    registry.corp.local:5000/windows/servercore:ltsc2025

# Push to private registry
docker push registry.corp.local:5000/windows/servercore:ltsc2025

# Pull from private registry on another host
docker pull registry.corp.local:5000/windows/servercore:ltsc2025

# List images in the registry using the API
Invoke-RestMethod -Uri "http://registry.corp.local:5000/v2/_catalog" | ConvertTo-Json

Step 7: Deploy Harbor as an Enterprise-Grade Alternative

For organisations that need role-based access control, image scanning, replication, and a web UI, Harbor is the recommended self-hosted registry. Deploy Harbor on a Linux host using its installer, then configure Windows Server 2025 to use it identically to the steps above — only the registry hostname changes.

# On a Linux host — quick Harbor setup (requires Docker Compose)
wget https://github.com/goharbor/harbor/releases/download/v2.11.0/harbor-online-installer-v2.11.0.tgz
tar xvf harbor-online-installer-v2.11.0.tgz
cd harbor
cp harbor.yml.tmpl harbor.yml
# Edit harbor.yml: set hostname, TLS certificate paths, admin password
sudo ./install.sh
# On Windows Server 2025 — push to Harbor
docker login harbor.corp.local -u admin
docker tag myapp:latest harbor.corp.local/myproject/myapp:latest
docker push harbor.corp.local/myproject/myapp:latest

Conclusion

A private container registry is an essential part of a secure container workflow, keeping proprietary images off public infrastructure and giving you full control over image provenance. On Windows Server 2025, the recommended pattern is to run the registry (Docker Registry v2 or Harbor) on a Linux host and configure the Windows Docker daemon to use it via the daemon.json file. For production use, always secure the registry with a valid TLS certificate and enable authentication. Harbor adds enterprise features like image vulnerability scanning and LDAP integration when your registry needs to scale beyond a simple development setup.