Skip to content

dotnet tool install / NuGet restore times out inside Docker bridge network; works in host mode and with curl #6844

@medoni

Description

@medoni

Describe the bug

Using mcr.microsoft.com/dotnet/sdk:8.0 the dotnet tool install and direct HttpClient calls to https://api.nuget.org/v3/index.json inside a container on Docker network mode bridge consistently time out. The same calls succeed when the container uses --network host. Direct curl requests inside the same container (bridge) also succeed, so this appears specific to the dotnet HTTP stack or NuGet client behavior inside Docker bridge networking rather than general network connectivity.

Which .NET image(s) are you using?

mcr.microsoft.com/dotnet/sdk:8.0

Steps to reproduce

  1. Using the SDK image, attempt to install a dotnet tool:

    docker run --rm -it --network bridge \
    mcr.microsoft.com/dotnet/sdk:8.0 \
    sh -c 'timeout 30 dotnet tool install -g dotnet-gcdump \
                --disable-parallel --no-cache --verbosity detailed \
            || echo "TIMEOUT" '

    Result: TIMEOUT.

    Expected result: dotnet tool install -g dotnet-gcdump should complete successfully and finish downloading packages from https://api.nuget.org/v3/index.json within a normal timeframe.

  2. Minimal HttpClient repro:

    docker run --rm -i --network bridge \
    mcr.microsoft.com/dotnet/sdk:8.0 \
    sh -c '
        # 1. Create project without repo
        dotnet new console -n httpTest -o /tmp/httpTest --force > /dev/null 2>&1
    
        # 2. create program.cs
        cat > /tmp/httpTest/Program.cs << "EOF"
    using System;
    using System.Net.Http;
    using System.Diagnostics;
    using System.Threading.Tasks;
    
    var sw = Stopwatch.StartNew();
    try
    {
        using var c = new HttpClient() { Timeout = TimeSpan.FromSeconds(30) };
        var r = await c.GetAsync("https://api.nuget.org/v3/index.json");
        Console.WriteLine($"HTTP {(int)r.StatusCode}  Body-Length: {r.Content.Headers.ContentLength}  Time: {sw.Elapsed.TotalSeconds:F2}s");
    }
    catch (Exception e)
    {
        Console.WriteLine($"EX after {sw.Elapsed.TotalSeconds:F2}s: {e.Message}");
    }
    EOF
    
        cd /tmp/httpTest
        dotnet build --no-restore -v q
        dotnet run --no-build
    '

    Result: EX after 30.03s: The request was canceled due to the configured HttpClient.Timeout of 30 seconds elapsing.

  3. curl from the same container (bridge) works:

    docker run --rm -it --network bridge \
    mcr.microsoft.com/dotnet/sdk:8.0 \
    sh -c 'curl -w "\ntime_total=%{time_total}s  http_code=%{http_code}  size=%{size_download}\n" \
                https://api.nuget.org/v3/index.json'

    Result: the expected json within 0.117710s

Other information

The full output of dotnet tool install after removing the timeout 30 command:

Unhandled exception: NuGet.Protocol.Core.Types.FatalProtocolException: Unable to load the service index for source https://api.nuget.org/v3/index.json.
 ---> System.TimeoutException: The HTTP request to 'GET https://api.nuget.org/v3/index.json' has timed out after 100000ms.
   at NuGet.Protocol.TimeoutUtility.StartWithTimeout[T](Func`2 getTask, TimeSpan timeout, String timeoutMessage, CancellationToken token)
   at NuGet.Protocol.HttpRetryHandler.SendAsync(HttpRetryHandlerRequest request, String source, ILogger log, CancellationToken cancellationToken)
   at NuGet.Protocol.HttpSource.GetThrottledResponse(Func`1 requestFactory, TimeSpan requestTimeout, TimeSpan downloadTimeout, Int32 maxTries, Boolean isRetry, Boolean isLastAttempt, Guid sessionId, ILogger log, CancellationToken cancellationToken)
   at NuGet.Protocol.HttpSource.<>c__DisplayClass15_0`1.<<GetAsync>b__0>d.MoveNext()
--- End of stack trace from previous location ---
   at NuGet.Common.ConcurrencyUtilities.ExecuteWithFileLockedAsync[T](String filePath, Func`2 action, CancellationToken token)
   at NuGet.Common.ConcurrencyUtilities.ExecuteWithFileLockedAsync[T](String filePath, Func`2 action, CancellationToken token)
   at NuGet.Protocol.HttpSource.GetAsync[T](HttpSourceCachedRequest request, Func`2 processAsync, ILogger log, CancellationToken token)
   at NuGet.Protocol.ServiceIndexResourceV3Provider.GetServiceIndexResourceV3(SourceRepository source, DateTime utcNow, ILogger log, CancellationToken token)
   --- End of inner exception stack trace ---
   at NuGet.Protocol.ServiceIndexResourceV3Provider.GetServiceIndexResourceV3(SourceRepository source, DateTime utcNow, ILogger log, CancellationToken token)
   at NuGet.Protocol.ServiceIndexResourceV3Provider.TryCreate(SourceRepository source, CancellationToken token)
   at NuGet.Protocol.Core.Types.SourceRepository.GetResourceAsync[T](CancellationToken token)
   at NuGet.Protocol.PackageMetadataResourceV3Provider.TryCreate(SourceRepository source, CancellationToken token)
   at NuGet.Protocol.Core.Types.SourceRepository.GetResourceAsync[T](CancellationToken token)
   at Microsoft.DotNet.Cli.NuGetPackageDownloader.NuGetPackageDownloader.GetPackageMetadataAsync(PackageSource source, String packageIdentifier, Boolean includePrerelease, Boolean includeUnlisted, CancellationToken cancellationToken)
   at Microsoft.DotNet.Cli.NuGetPackageDownloader.NuGetPackageDownloader.<>c__DisplayClass24_0.<GetMatchingVersionInternalAsync>b__0(PackageSource source)
   at System.Linq.Enumerable.SelectListIterator`2.Fill(ReadOnlySpan`1 source, Span`1 destination, Func`2 func)
   at System.Linq.Enumerable.SelectListIterator`2.ToArray()
   at System.Linq.Enumerable.ToArray[TSource](IEnumerable`1 source)
   at Microsoft.DotNet.Cli.NuGetPackageDownloader.NuGetPackageDownloader.GetMatchingVersionInternalAsync(String packageIdentifier, IEnumerable`1 packageSources, VersionRange versionRange, CancellationToken cancellationToken)
   at Microsoft.DotNet.Cli.NuGetPackageDownloader.NuGetPackageDownloader.GetBestPackageVersionAsync(PackageId packageId, VersionRange versionRange, PackageSourceLocation packageSourceLocation)
   at Microsoft.DotNet.Cli.ToolPackage.ToolPackageDownloader.<>c__DisplayClass8_0.<InstallPackage>b__0()
   at Microsoft.DotNet.Cli.TransactionalAction.Run[T](Func`1 action, Action commit, Action rollback)
   at Microsoft.DotNet.Tools.Tool.Install.ToolInstallGlobalOrToolPathCommand.<>c__DisplayClass24_0.<ExecuteInstallCommand>b__1()
   at Microsoft.DotNet.Tools.Tool.Install.ToolInstallGlobalOrToolPathCommand.RunWithHandlingInstallError(Action installAction, PackageId packageId)
   at Microsoft.DotNet.Tools.Tool.Install.ToolInstallGlobalOrToolPathCommand.ExecuteInstallCommand(PackageId packageId)
   at Microsoft.DotNet.Tools.Tool.Install.ToolInstallGlobalOrToolPathCommand.Execute()
   at System.CommandLine.Invocation.InvocationPipeline.Invoke(ParseResult parseResult)
   at Microsoft.DotNet.Cli.Program.ProcessArgs(String[] args, TimeSpan startupTime, ITelemetry telemetryClient)

Output of docker version

Client: Docker Engine - Community
 Version:           28.5.1
 API version:       1.51
 Go version:        go1.24.8
 Git commit:        e180ab8
 Built:             Wed Oct  8 12:17:26 2025
 OS/Arch:           linux/amd64
 Context:           default

Server: Docker Engine - Community
 Engine:
  Version:          28.5.1
  API version:      1.51 (minimum version 1.24)
  Go version:       go1.24.8
  Git commit:       f8215cc
  Built:            Wed Oct  8 12:17:26 2025
  OS/Arch:          linux/amd64
  Experimental:     false
 containerd:
  Version:          v1.7.28
  GitCommit:        b98a3aace656320842a23f4a392a33f46af97866
 runc:
  Version:          1.3.0
  GitCommit:        v1.3.0-0-g4ca628d1
 docker-init:
  Version:          0.19.0
  GitCommit:        de40ad0

Output of docker info

Client: Docker Engine - Community
 Version:    28.5.1
 Context:    default
 Debug Mode: false
 Plugins:
  buildx: Docker Buildx (Docker Inc.)
    Version:  v0.29.1
    Path:     /usr/libexec/docker/cli-plugins/docker-buildx
  compose: Docker Compose (Docker Inc.)
    Version:  v2.40.3
    Path:     /usr/libexec/docker/cli-plugins/docker-compose

Server:
 Containers: 9
  Running: 1
  Paused: 0
  Stopped: 8
 Images: 68
 Server Version: 28.5.1
 Storage Driver: overlay2
  Backing Filesystem: extfs
  Supports d_type: true
  Using metacopy: false
  Native Overlay Diff: true
  userxattr: false
 Logging Driver: json-file
 Cgroup Driver: systemd
 Cgroup Version: 2
 Plugins:
  Volume: local
  Network: bridge host ipvlan macvlan null overlay
  Log: awslogs fluentd gcplogs gelf journald json-file local splunk syslog
 CDI spec directories:
  /etc/cdi
  /var/run/cdi
 Swarm: inactive
 Runtimes: io.containerd.runc.v2 runc
 Default Runtime: runc
 Init Binary: docker-init
 containerd version: b98a3aace656320842a23f4a392a33f46af97866
 runc version: v1.3.0-0-g4ca628d1
 init version: de40ad0
 Security Options:
  seccomp
   Profile: builtin
  cgroupns
 Kernel Version: 6.6.87.2-microsoft-standard-WSL2
 Operating System: Ubuntu 24.04.3 LTS
 OSType: linux
 Architecture: x86_64
 CPUs: 24
 Total Memory: 31.34GiB
 Name: N12
 ID: 4354539a-71e4-46be-b2b9-b8a060380f81
 Docker Root Dir: /var/lib/docker
 Debug Mode: false
 Experimental: false
 Insecure Registries:
  registry.k3s.local.test
  ::1/128
  127.0.0.0/8
 Live Restore Enabled: false

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Projects

    Status

    Done

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions