Skip to content

Process.Start() and PowerShell Start-Process hang on newer mcr.microsoft.com/windows/servercore:ltsc2019 / .NET Framework 4.8 LTSC2019 images #645

Open

Description

Description of the bug

We are seeing a regression in newer Windows Server Core LTSC 2019 container images.

Starting a child process from inside the container hangs indefinitely. This affects both:

  • .NET System.Diagnostics.Process.Start()
  • PowerShell Start-Process

This used to work a few weeks ago and still works on an older pinned .NET Framework SDK image.

Known-good image

The following images still works correctly:

  • mcr.microsoft.com/dotnet/framework/sdk:4.8-20260512-windowsservercore-ltsc2019
  • mcr.microsoft.com/windows/servercore:10.0.17763.8880

On this image, both:

  • .NET Process.Start()
  • PowerShell Start-Process

return normally.

Regressed images

The problem occurs on newer images based on updated LTSC2019 Server Core, including:

  • mcr.microsoft.com/windows/servercore:ltsc2019
  • current floating mcr.microsoft.com/dotnet/framework/sdk:4.8
  • current floating mcr.microsoft.com/dotnet/framework/sdk:4.8-windowsservercore-ltsc2019

Expected behavior

Starting cmd.exe /c echo hello should return immediately.

Expected output for the .NET repro:

Before
After

Expected output for the PowerShell repro:

Before
After

Actual behavior

The process-start call hangs indefinitely.

For the .NET repro, output is:

Before

and Process.Start() never returns.

For the PowerShell repro, output is:

Before

and Start-Process never returns.

Container OS versions observed

Known-good older image lineage:

  • 10.0.17763.8880

Failing newer image lineage:

  • 10.0.17763.9020

Minimal PowerShell repro

Run:

docker run -ti --rm mcr.microsoft.com/windows/servercore:ltsc2019 powershell

Inside the container:

Write-Host "Before"
Start-Process cmd.exe -ArgumentList '/c echo hello' -Wait
Write-Host "After"

Actual result on the failing image:

Before

and then it hangs.

Minimal .NET repro

Run:

docker run -ti --rm mcr.microsoft.com/windows/servercore:ltsc2019 powershell

Inside the container:

Add-Type @"
using System;
using System.Diagnostics;

public class Test {
    public static void Run() {
        var p = new Process();
        p.StartInfo.FileName = "cmd.exe";
        p.StartInfo.Arguments = "/c echo hello";
        Console.WriteLine("Before");
        p.Start();
        Console.WriteLine("After");
    }
}
"@

[Test]::Run()

Actual result on the failing image:

Before

and then it hangs.

Notes

  • This does not appear to be specific to .NET Framework, because the same behavior is reproducible on plain mcr.microsoft.com/windows/servercore:ltsc2019.
  • The regression appears to have been introduced after the 2026-05-12 .NET Framework SDK LTSC2019 image.
  • We are currently pinning to mcr.microsoft.com/dotnet/framework/sdk:4.8-20260512-windowsservercore-ltsc2019 as a temporary workaround.

Environment

  • Host OS: Windows Server 2019
  • Container base: mcr.microsoft.com/windows/servercore:ltsc2019
  • Affected higher-level image: mcr.microsoft.com/dotnet/framework/sdk:4.8
  • Working higher-level image: mcr.microsoft.com/windows/servercore:10.0.17763.8880

Question

Is this a known regression in the updated LTSC2019 Server Core image, or an intentional change in process creation behavior inside Windows containers?

Activity

  1. github-actions commented on Aug 7, 2026

    @github-actions

    Thank you for creating an Issue. Please note that GitHub is not an official channel for Microsoft support requests. To create an official support request, please open a ticket here. Microsoft and the GitHub Community strive to provide a best effort in answering questions and supporting Issues on GitHub.

  2. microsoft-github-policy-service commented on Sep 9, 2026

    @microsoft-github-policy-service
    Contributor

    This issue has been open for 30 days with no updates.
    no assignees, please provide an update or close this issue.

  3. jy-cimcloud commented on Sep 15, 2026

    @jy-cimcloud

    Independently hit this and can confirm, with three additions.

    1. It is not an indefinite hang — it is a flat ~1800 s timeout.

    The call does return, after almost exactly 30 minutes. Four runs across three
    images all landed between 1801 and 1802 seconds, which is a hard timeout rather
    than variance. Anyone who gives up before the 30-minute mark reads this as a
    hang, which I think is why it has been reported that way.

    2. 10.0.17763.9020 already reproduces, which narrows it to one servicing step.

    Combined with your known-good 8880, and since there was no June 2026 release,
    8880 and 9020 are consecutive updates.

    Windows build Image TotalSeconds
    10.0.17763.8880 dotnet/framework/runtime:4.8-20260512-windowsservercore-ltsc2019 1.43
    10.0.17763.9020 dotnet/framework/runtime:4.8-20260714-windowsservercore-ltsc2019 1801.75
    10.0.17763.9245 windows/servercore:ltsc2019 1802.01

    Repro, on a Server 2019 host with process isolation:

    docker run --rm mcr.microsoft.com/windows/servercore:ltsc2019 powershell -NoProfile -Command "Measure-Command { Start-Process C:\Windows\SysWOW64\regsvr32.exe -ArgumentList '/s','C:\Windows\SysWOW64\scrrun.dll' -Wait } | Select TotalSeconds"

    3. -NoNewWindow avoids it, which may point at the shell-launch path.

    Same container, minutes apart:

    Variant Launch path Time
    Start-Process ... -Wait ShellExecuteEx (PowerShell default) ~1806 s
    Start-Process ... -Wait -NoNewWindow CreateProcess 1.03 s

    -NoNewWindow switches Start-Process from UseShellExecute = $true to
    CreateProcess. Since .NET Framework's ProcessStartInfo.UseShellExecute also
    defaults to true, that would explain why both of your repros hang. It predicts
    that Process.Start() with UseShellExecute = false will not — worth confirming,
    since it would separate the shell path from process creation.

    Also possibly useful: during the stall, the target process never appears in
    tasklist inside the container at all, so it looks like the block happens before
    the child is created.

    Other things I ruled out here, in case it saves someone repeating them: it is not
    the target binary (reproduces on stock in-image scrrun.dll), not a network wait
    (netstat in the stalled container shows completed connections in TIME_WAIT,
    nothing in SYN_SENT), and not the host — 8880 returns in 1.4 s on the same
    host, same hour, same Docker daemon.

    Environment: Windows Server 2019 host, process isolation, Docker 20.10.9 (API 1.41).

    Disclosure: I used Claude (Anthropic) to help investigate this and draft this
    comment. The measurements were run on my own host and I have checked them.

  4. mariskahoogenboom commented on Sep 16, 2026

    @mariskahoogenboom
    Author

    Thanks for the workaround with the -NoNewWindow. That's quite useful.

  5. bobsira commented on Oct 7, 2026

    @bobsira

    Investigation update

    Building on the shell-launch observations above, I reproduced the delay and traced it to a StateRepository startup failure associated with registry permissions in the host-prepared container image.

    Reproduction and failure path

    Using pinned Server Core images, the original .NET Framework Process.Start() repro completed in approximately 0.56 seconds on 17763.8880 versus 1,801 seconds on the cached 17763.9020 image. The latter run included diagnostic dump captures.

    CreateProcessW, UseShellExecute=false, and Start-Process -NoNewWindow completed promptly. Native ShellExecuteExW and direct activation of Windows.Internal.StateRepository.FileTypeAssociation reproduced the stall, independently of PowerShell/.NET.

    StateRepository logged event 227, 0x80070005 / access denied, checkpoint 850, during cache initialization. The denied registry open targeted the 64-bit key:

    HKLM\SOFTWARE\Microsoft\Windows\CurrentVersion\AppModel\StateRepository\Cache
    

    In the failing cached image, this key had no explicit access-control entries, and SYSTEM inherited read-only access rather than the write permissions needed for initialization. Restoring the known-good key DACL in disposable test containers restored both activation and the original shell-based launch in two independent comparisons. This was a diagnostic experiment, not a recommended production ACL repair.

    Where the permissions were lost

    The 17763.9020 source delta hive already contained the required permissions; they were missing from the older host-prepared merged hive.

    Repeated controlled imports using identical inputs and changing only wc_storage.dll produced:

    Storage DLL version Explicit Cache permission entries after importing 17763.9020
    17763.3532 0 — reproduces the failing cached descriptor
    17763.9121 8 — preserves the source permissions

    An important qualification: the original cached hives were created before the host servicing reboot completed. Their failure on the subsequently updated host did not establish that fresh imports on that host also fail.

    End-to-end confirmation

    On Server 2019 host 17763.9245, Docker 29.8.1, I imported the same pinned images into a verified empty, separate Docker store, preserving the original failing store. Parent/delta hive inputs were byte-identical; no registry permissions were manually changed in this comparison.

    Results for 17763.9020:

    Image store / isolation StateRepository activation Original Process.Start()
    Existing cache / process Still blocked after 60 s Still blocked after 60 s
    Fresh import / process 0.588 s 0.521 s
    Fresh import / Hyper-V 2.308 s 4.200 s

    Successful launches returned child exit code zero. Fresh imports retained all eight explicit Cache permission entries. Timings exclude container preparation; nested Hyper-V results are not a performance comparison.

    Current conclusion and remaining work

    The evidence supports an older host-import permission-loss problem exposed by this image, with the damaged prepared hive persisting across host servicing. A fresh import on the tested serviced host avoids the reproduced failure. An ordinary pull into an existing store may reuse the affected layers.

    The earliest corrected host version, a supported remediation for existing caches, and the complete ~30-minute activation timeout mechanism remain unresolved. The 17763.9245 container image was not included in this targeted comparison. These findings should not yet be treated as confirmation that every reported environment is resolved.

  6. bobsira commented on Oct 12, 2026

    @bobsira

    Follow-up: native tracing confirms the permission-loss mechanism

    Following up on my earlier investigation update, I have now directly traced the relevant OfflineHiveLib::MergeHive path in wc_storage.dll, rather than relying only on output comparisons and static disassembly.

    Observed behavior

    Using identical copied parent/delta hives from the pinned 17763.9020 image, both tested implementations:

    1. Read metadata 0x00000004 for the StateRepository Cache key.
    2. Delete the existing destination key successfully.
    3. Recreate it with a null security-descriptor argument.

    The behavior then differs:

    Storage DLL Behavior after key creation Final explicit Cache ACEs
    17763.3532 Continues without the newer source-security copy 0
    17763.9121 EvaluateCurrentState returns 1; CopySecurityDescriptor copies source security to the newly created key and returns success 8

    The separate security-copy path at recursive entry checks bit 0x2. The observed metadata remains 0x4, so that path is skipped and does not compensate for the older implementation’s missing new-key copy.

    The newer decision is associated, through exact-binary symbol/disassembly analysis, with:

    Servicing_ContainerRollupHiveKeySecurity
    Feature ID: 63295035
    

    The affirmative decision was observed at this call site in our runs; this does not establish feature availability or configuration on other hosts. The internal metadata flag names and the image-producing change remain unidentified.

    Repeatability and preservation

    Two independent old/new trace pairs reproduced this sequence. Separate uninstrumented good/bad controls reproduced the same output permissions. Independent offline inspection of the saved hives confirmed exact matches for the inspected owner/group/DACL: the older output matches the original failing cache, while the newer output matches the intact source delta.

    All eight imports completed successfully—so import success alone does not detect this defect. Installed Windows binaries, original Docker image-store hives, and preserved source inputs were unchanged. No servicing-feature override or manual registry-permission change was applied in this phase.

    Implication and remaining work

    This strengthens the explanation that the reproduced launch delay originates from permissions lost during older host-side hive preparation. It complements the earlier end-to-end result: a genuinely fresh Docker import on the tested serviced host restores activation and the original launch, while its older cached copy still fails.

    17763.9121 is a successfully tested DLL version, not an established minimum fixed host release. The earliest corrected Server 2019 servicing release, supported recovery for already-imported affected layers, and complete approximately 30-minute activation timeout remain unresolved.

    This is not a recommendation to replace DLLs, toggle the feature, or copy ACLs in production.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't workingtriageNew and needs attention

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions