September 20, 2026

Fixing Sitecore Docker on Windows 11 with LTSC2022

A Step-by-Step Guide to Migrating Container Base Images from LTSC2019 to LTSC2022 and Resolving Build Failures

Posted on September 20, 2026  •  10 minutes  • 2006 words

Table of contents

📊 Introduction

Moving a Sitecore 10.4 local container environment from older Windows Server 2019 base images to LTSC2022 is more than just swapping 1809 for ltsc2022 in your image tags.

Windows 11 manages container isolation and system processes differently than earlier releases. If your Docker daemon is set to the wrong OS, if base images mismatch across your services, or if build stages run out of sequence, local builds will crash before containers even initialize.

This guide turns a real troubleshooting session into a repeatable, step-by-step migration blueprint. We will cover the quick system checks to perform first, how to fix Docker Desktop permissions, and how to centralize your environment variables so future updates take only minutes.


⚠️ Why Sitecore Docker Builds Fail on Windows 11

Sitecore 10.4 instances rely on Windows containers. When you upgrade a development machine to Windows 11, older project configurations can trigger several hidden build issues:

  • Outdated Nano Server Images: Older images hardcoded to 1809 that fail under Windows 11 container isolation.
  • Mismatched Image Families: Mixing older ltsc2019 tags with newer ltsc2022 tags across child services.
  • Wrong Daemon Mode: Docker Desktop running its default Linux daemon instead of Windows containers.
  • Missing Background Service: Docker Desktop installed per-user (%LOCALAPPDATA%), missing the privileged service required by Windows containers.
  • Out-of-Order Builds: Downstream services trying to build before local base images exist.
  • Accidental Variable Expansion: Database passwords containing $ signs being parsed as missing Compose variables.

BuildKit Error Hunting

Because Docker BuildKit builds targets in parallel, multiple errors often show up at the same time. Always scroll up to the very first error in your terminal. Messages such as ‘context canceled’ are simply secondary side effects of that initial failure.


🔍 Step 1: Check the Docker Daemon Before Editing Code

Before changing your Dockerfiles or configuration, verify that Docker Desktop is actually set up to run Windows containers.

Run this command in PowerShell:

docker info | Select-String "OSType"

You need to see:

OSType: windows

If the output is OSType: linux, Docker Desktop is running its Linux daemon. Setting platform: windows/amd64 in your docker-compose.yml does not change the daemon engine for you. Trying to build a Windows image on a Linux daemon produces misleading errors:

unable to find user ContainerUser: no matching entries in passwd file

Next, confirm your exact host OS build version:

Get-ComputerInfo | Select-Object WindowsProductName, WindowsVersion, OsBuildNumber

Windows 11 supports Windows Server 2022 containers natively, but the Docker Desktop daemon must be in Windows mode before running builds.


📦 Step 2: Install Docker Desktop the Right Way

Docker Desktop has two installation modes on Windows, and the difference is critical:

  • Per-User Installation: Installs in %LOCALAPPDATA%\Programs\DockerDesktop. It supports Linux containers only.
  • All-Users Installation: Installs in C:\Program Files\Docker\Docker. It installs the privileged system helper and fully supports Windows containers.

For an x64 Windows 11 workstation running Sitecore, download the x86_64 installer and run it with administrator permissions for all users. Do not choose the ARM build unless you are on an ARM64 machine.

The all-users install registers com.docker.service, which is required for Windows containers and Hyper-V isolation. Verify that it is running:

Get-Service com.docker.service

If another administrator set up your computer, add your account to the local docker-users group:

net localgroup docker-users "DOMAIN\username" /add

Session Token Refresh

Sign out and sign back in after changing group membership so Windows creates a new security token for your user account.

Confirm your membership:

whoami /groups | Select-String "docker-users"

If you are running Docker inside a virtual machine (VM) or virtual desktop infrastructure (VDI), make sure nested virtualization is enabled in the hypervisor host settings. Enabling Hyper-V inside the guest OS alone will not work if the host hypervisor blocks virtualization extensions.


⚙️ Step 3: Align the Solution with LTSC2022

Instead of scattering hardcoded image tags across multiple Dockerfiles, centralize your image tags inside your .env file:

SITECORE_VERSION=10.4.1-ltsc2022
EXTERNAL_IMAGE_TAG_SUFFIX=ltsc2022
TRAEFIK_IMAGE=traefik:v3.6.4-windowsservercore-ltsc2022
NODEJS_PARENT_IMAGE=mcr.microsoft.com/windows/nanoserver:ltsc2022
ARTIFACTS_IMAGE=mcr.microsoft.com/windows/nanoserver:ltsc2022
WINDOWS_CONTAINER_PLATFORM=windows/amd64
NODEJS_VERSION=24.2.0
TRAEFIK_ISOLATION=hyperv

# Othe required changes for Sitecore LTSC2022 images
TOOLS_IMAGE=scr.sitecore.com/tools/sitecore-docker-tools-assets:10.2.0-ltsc2022
MANAGEMENT_SERVICES_IMAGE=scr.sitecore.com/sxp/modules/sitecore-management-services-xm1-assets:5.2.113-ltsc2022
HEADLESS_SERVICES_IMAGE=scr.sitecore.com/sxp/modules/sitecore-headless-services-xm1-assets:22.0.7-ltsc2022

# Sitecore SXA and SPE versions.
SPE_VERSION=7.0-ltsc2022
SXA_VERSION=10.4.0-ltsc2022
TOOLS_VERSION=10.4.0-ltsc2022
SITECORE_TOOLS_REGISTRY=scr.sitecore.com/tools/
# -- Headless SXA -- :: End :: ---

SITECORE_ID_VERSION=8.0-ltsc2022

Pass those values cleanly through docker-compose.yml:

services:
  traefik:
    isolation: ${TRAEFIK_ISOLATION}
    image: ${TRAEFIK_IMAGE}
  id:
    isolation: ${ISOLATION}
    image: ${SITECORE_DOCKER_REGISTRY}sitecore-identity:${SITECORE_ID_VERSION}
    environment:
     Sitecore_Sitecore__IdentityServer__SitecoreMemberShipOptions__ConnectionString: Data Source=${SQL_SERVER};Initial Catalog=${SQL_DATABASE_PREFIX}.Core;User ID=${SQL_SA_LOGIN};Password=${SQL_SA_PASSWORD};Encrypt=true;TrustServerCertificate=true;
    labels:
    # ADD THIS EXACT LINE BELOW FOR TRAEFIK V3:
      - "traefik.http.services.id.loadbalancer.server.port=80"
 cd:
    labels:
    # ADD THIS EXACT LINE BELOW FOR TRAEFIK V3:
      - "traefik.http.services.cd.loadbalancer.server.port=80"
 cm:
    labels:
    # ADD THIS EXACT LINE BELOW FOR TRAEFIK V3:
      - "traefik.http.services.cm.loadbalancer.server.port=80"  

Update for the docker-compose-override.yml:

services:
  nodejs:
    platform: ${WINDOWS_CONTAINER_PLATFORM}
    isolation: ${SERVICE_ISOLATION}
    build:
      context: ./docker/build/nodejs
      args:
        PARENT_IMAGE: ${NODEJS_PARENT_IMAGE}

  solution:
    platform: ${WINDOWS_CONTAINER_PLATFORM}
    build:
      context: ./
      args:
        ARTIFACTS_IMAGE: ${ARTIFACTS_IMAGE}
  rendering:
    platform: ${WINDOWS_CONTAINER_PLATFORM}
    labels:
    # ADD THIS EXACT LINE BELOW FOR TRAEFIK V3:
      - "traefik.http.services.rendering-nextjs.loadbalancer.server.port=3000"
  # Mount the Traefik configuration and certs.
  traefik:
    platform: ${WINDOWS_CONTAINER_PLATFORM}
  mssql:
    platform: ${WINDOWS_CONTAINER_PLATFORM}
  mssql-init:
    platform: ${WINDOWS_CONTAINER_PLATFORM}
  solr:
    platform: ${WINDOWS_CONTAINER_PLATFORM}
  solr-init:
    platform: ${WINDOWS_CONTAINER_PLATFORM}
  id:
    platform: ${WINDOWS_CONTAINER_PLATFORM}
    image: ${REGISTRY}${COMPOSE_PROJECT_NAME}-id8:${VERSION:-latest}
    build:
      context: ./docker/build/id
      args:
        PARENT_IMAGE: ${SITECORE_DOCKER_REGISTRY}sitecore-identity:${SITECORE_ID_VERSION}
  cd:
    platform: ${WINDOWS_CONTAINER_PLATFORM}
    image: ${REGISTRY}${COMPOSE_PROJECT_NAME}-xm1-cd:${VERSION:-latest}
    build:
      context: ./docker/build/cd
      args:
        TOOLS_IMAGE: ${SITECORE_TOOLS_REGISTRY}sitecore-docker-tools-assets:${TOOLS_VERSION}
    depends_on:
      solution:
        condition: service_started
 cm:
    platform: ${WINDOWS_CONTAINER_PLATFORM}
    build:
      context: ./docker/build/cm
      args:
        TOOLS_IMAGE: ${SITECORE_TOOLS_REGISTRY}sitecore-docker-tools-assets:${TOOLS_VERSION}

You can also enforce required variables directly in Compose. This fails early with a clear warning instead of passing an empty image string:

ARTIFACTS_IMAGE: ${ARTIFACTS_IMAGE:?ARTIFACTS_IMAGE is required}

Review Every Multi-Stage Dockerfile

Multi-stage builds can easily hide outdated base tags. Declare any build arguments used in FROM statements before the first stage:

ARG BUILD_IMAGE
ARG ARTIFACTS_IMAGE

FROM ${BUILD_IMAGE} AS builder
# Solution compile steps

FROM ${ARTIFACTS_IMAGE}
COPY --from=builder /build/artifacts /artifacts

An ARG declared inside one stage is not available to later FROM statements. If an argument is missing or out of scope, BuildKit will throw an error:

base name (${ARTIFACTS_IMAGE}) should not be blank

Upgrade Components Thoughtfully

Not every image should be upgraded at the same time:

  • Nano Server: Update older 1809 tags to ltsc2022 where your tools support it.
  • .NET Framework SDK: Review this separately. If your build uses 4.8-windowsservercore-ltsc2019, make sure your custom build scripts compile cleanly under LTSC2022 before swapping tags.
  • Reverse Proxies (Traefik): Moving from Traefik v2 to v3 is a major version upgrade with breaking configuration changes. Do not bundle it with an OS migration unless necessary.
  • Node.js: Verify that your front-end rendering packages and plugins work with newer Node versions before upgrading.

🧪 Step 4: Validate Variable Resolution First

Before triggering a lengthy build, verify how Docker Compose interprets your variables:

docker compose config --environment

Then check the merged Compose configuration:

docker compose `
  -f .\docker-compose.yml `
  -f .\docker-compose.override.xm1.yml `
  config

This step highlights invalid YAML formatting, missing variables, or broken paths in seconds.

Handling Dollar Signs in Passwords

If you see a warning like ‘The “G” variable is not set’, check your passwords or secret tokens. Docker Compose treats $G as a variable. Escape dollar signs by doubling them ($$) in your configuration files or store secrets safely in your .env file.


🏗️ Step 5: Build Local Dependency Images First

Some Dockerfiles depend on custom local images created earlier in your solution. For example, your rendering container may rely on your custom Node.js image, while your Content Management (CM) container copies files from your local solution build.

If Docker Compose builds services out of order, it may try to pull your local image name (like myproject-solution:latest) from Docker Hub:

pull access denied for myproject-solution, repository does not exist

To prevent this, build your foundation dependency images first:

# Step 1: Build local base and asset images
docker compose `
  -f .\docker-compose.yml `
  -f .\docker-compose.override.xm1.yml `
  build nodejs solution

# Step 2: Build the remaining services
docker compose `
  -f .\docker-compose.yml `
  -f .\docker-compose.override.xm1.yml `
  build

🛠️ Common Errors & Solutions

Error or WarningLikely CauseFirst Check
no match for platform in manifestWrong daemon OS, bad image tag, or platform mismatchRun docker info and inspect the exact image tag
unable to find user ContainerUserWindows image running against a Linux daemonConfirm OSType: windows before editing the Dockerfile
base name (...) should not be blankMissing build argument or out-of-scope Dockerfile ARGRun docker compose config; declare ARG before the first FROM
pull access denied for a local imageDependent image was not built locally firstBuild base service images (solution, nodejs) first
com.docker.service is missingDocker Desktop was installed per-userReinstall Docker Desktop with admin rights for all users
User not in docker-usersMissing local group rights or stale sign-in tokenRun net localgroup docker-users, then sign out and back in
context canceledA different parallel build step failed firstFind the earliest non-cancellation error in console output
Compose says variable is unsetA $ in a password was treated as variable interpolationRun docker compose config --environment and escape $ with $$

📋 Safe Validation Checklist

Run these checks in order whenever setting up or troubleshooting a machine:

# 1. Confirm host OS and daemon mode
Get-ComputerInfo | Select-Object WindowsProductName, WindowsVersion, OsBuildNumber
docker info | Select-String "OSType"

# 2. Verify Docker service and local permissions
Get-Service com.docker.service
net localgroup docker-users

# 3. Resolve Compose variables and validate syntax
docker compose config --environment
docker compose -f .\docker-compose.yml -f .\docker-compose.override.xm1.yml config --quiet

# 4. Confirm LTSC2022 variables are set
Get-Content .env | Select-String "SITECORE_VERSION|EXTERNAL_IMAGE_TAG_SUFFIX|NODEJS_PARENT_IMAGE|ARTIFACTS_IMAGE"

# 5. Build base dependency images before the full topology
docker compose -f .\docker-compose.yml -f .\docker-compose.override.xm1.yml build nodejs solution

# 6. Build and start the environment
docker compose -f .\docker-compose.yml -f .\docker-compose.override.xm1.yml build
docker compose -f .\docker-compose.yml -f .\docker-compose.override.xm1.yml up -d

Avoid Unnecessary Pruning

Avoid running ‘docker system prune –all –volumes’ as a routine diagnostic step. It wipes all cached layers, downloaded images, and database volumes across all your local projects.


💬 Frequently Asked Questions (FAQs)


No. Docker Desktop runs either the Linux daemon or the Windows daemon at any given time. Sitecore XM/XP requires the Windows daemon. If you need Linux containers alongside it, run them on a separate Linux VM or via WSL2 on another machine.

Windows 11 shares its kernel architecture with Windows Server 2022. While Windows 10 could run older 1809 images under process isolation or standard Hyper-V isolation, Windows 11 provides the most stable, performant, and supported developer experience using Windows Server 2022 (LTSC2022) base containers.

This happens when a downstream service depends on a local image that has not been created yet. Because Docker cannot find the image locally, it assumes it lives on Docker Hub and tries to download it. Building your dependency images (such as solution and nodejs) before running docker compose up fixes this issue.

Yes. A per-user installation (inside %LOCALAPPDATA%) does not install the privileged background service (com.docker.service) needed for Windows containers and Hyper-V isolation. You must reinstall Docker Desktop using the system-wide installer with administrator rights.

Docker Compose uses bash-style variable substitution, so any $VARIABLE or ${VARIABLE} syntax is evaluated as an environment variable. If your password contains a dollar sign (e.g., P@

word),escapeeverydollarsignbytypingtwo(word), escape every dollar sign by typing two (

) in your configuration files, or store the password securely in your .env file.


🏁 Conclusion

A successful Sitecore Docker LTSC2022 migration starts by proving that Docker Desktop is installed system-wide and actively running in Windows container mode. Once you confirm OSType: windows, aligning your image tags, centralizing variables in .env, validating your Compose configuration, and building foundation dependencies in the right order will keep your local developer environment stable and fast.

Following this sequence prevents hours of unnecessary Dockerfile edits caused by symptoms of the wrong daemon mode or missing system services.


🧾 Credit/References

View All

comments powered by Disqus
All posts