Who this helps: teams running dotnet restore on a remote Mac where nuget.org or upstream feeds traverse a long-haul path. You get: an ordered NuGet.Config mirror chain, MaxHttpRequestsPerSource starters, globalPackagesFolder placement, RestoreLockedMode with packages.lock.json, and Central Package Management (CPM) knobs. Start from the blog index, our homepage, and related JVM mirror matrix—no login required to read.
  • Flat feed lists: every package hits nuget.org first, so internal feeds never short-circuit the graph.
  • Unbounded HTTP fan-out: default parallel downloads per source overwhelm shared NVMe with tiny random writes.
  • Drift without locks: CI restores “whatever resolves today,” so cross-border retries mask version slides until release week.

① Decision matrix: pick defaults before tuning parallelism

Use this table as a checklist. Adjust one column at a time; changing mirrors and maxHttpRequestsPerSource together makes regressions hard to bisect.

Scenario Mirror / feed strategy MaxHttpRequestsPerSource globalPackagesFolder RestoreLockedMode
Corp Azure Artifacts + nuget.org Named upstream in Artifacts; packageSourceMapping pins Microsoft.* to upstream, internal IDs to Artifacts 8 shared pool; 4 if disk queue rises /usr/local/ci/nuget/global-packages via NUGET_PACKAGES true in CI after packages.lock.json is committed
Self-hosted Nexus / BaGet mirror Mirror feed first, nuget.org explicit fallback only if policy allows 6–8; lower if mirror CPU spikes Same pool path; prune old package folders off-peak true for release branches; optional false on experimental branches
OSS-only, weak cross-border Approved regional mirror (if legal), else nuget.org with long timeouts 4 first; raise only when p95 restore stable Local SSD only—never SMB home true once graph stabilizes; regenerate lock after bumps
CPM monorepo (Directory.Packages.props) Mapping must reference the same source names as non-CPM Same as rows above—CPM does not reduce HTTP count by itself Shared global folder still wins for dedupe Pair with lock file per project or central props as your SDK version supports

② NuGet.Config mirror chain: executable skeleton

Name every feed, map package IDs explicitly, and keep globalPackagesFolder beside your build roots on the Mac mini SSD. The pattern below is representative—swap URLs for your approved endpoints.

<?xml version="1.0" encoding="utf-8"?>
<configuration>
  <config>
    <add key="globalPackagesFolder" value="/usr/local/ci/nuget/global-packages" />
    <add key="maxHttpRequestsPerSource" value="8" />
  </config>
  <packageSources>
    <clear />
    <add key="corp" value="https://pkgs.dev.azure.com/your-org/_packaging/main/nuget/v3/index.json" />
    <add key="nugetorg" value="https://api.nuget.org/v3/index.json" />
  </packageSources>
  <packageSourceMapping>
    <packageSource key="corp">
      <package pattern="Contoso.*" />
    </packageSource>
    <packageSource key="nugetorg">
      <package pattern="*" />
    </packageSource>
  </packageSourceMapping>
</configuration>

For CI, prefer NUGET_PACKAGES or dotnet restore --configfile ci.NuGet.Config so developer machines keep personal defaults. On macOS agents, install the same credential provider you use on Windows/Linux (for example the Azure Artifacts provider) and inject short-lived tokens through environment variables or secret stores—never commit PATs into Config. Warm the cache with a no-op restore against a sample project before enabling locked mode across the whole solution so the first failing package ID is obvious in logs.

Align with the broader dependency story in cross-border Git, npm, and Homebrew CI optimization when the same runner also pulls front-end tooling.

③ MSBuild restore properties vs NuGet.Config

Knob Where to set Typical CI value Notes
maxHttpRequestsPerSource NuGet.Config <config> 4–8 Caps concurrent requests per feed; primary lever for SSD smoothing
RestoreMaxHttpRequestsPerSource MSBuild / CLI -p: Mirror Config or override per job Use when pipeline templates cannot write Config files
RestoreLockedMode CI only Directory.Build.props or CLI true Requires committed packages.lock.json; fails on drift—intentional
RestorePackagesWithLockFile Project or props true Emits lock file; pair with PR discipline
ContinuousIntegrationBuild CI environment true Stabilizes asset metadata; set alongside locked restore

Copy-paste CI line (adjust props path as needed):

dotnet restore /p:RestoreLockedMode=true /p:ContinuousIntegrationBuild=true

④ Central Package Management: lock-friendly parameter list

CPM moves versions to Directory.Packages.props; restores still download packages through the same HTTP stack. Keep mapping and feeds identical to non-CPM repos so security reviews stay boring.

Artifact Purpose CI implication
Directory.Packages.props Central PackageVersion items Single bump surface; still needs mirror health
ManagePackageVersionsCentrally Enables CPM in Directory.Build.props Set once per repo root
CentralPackageTransitivePinningEnabled Optional: pin transitive versions from Directory.Packages.props SDK-gated; enable only after lock files are green in CI
PackageVersionOverride in projects Escape hatch per project Document overrides—locked CI will fail on sneaky edits

If you also run Go workloads on the same pool, compare IO profiles with Go modules GOPROXY matrix so module download storms do not line up with every dotnet restore on the hour.

⑤ Five-step rollout on a remote Mac agent

1
Pin SDK. Commit global.json with an exact feature band so lock file and CPM behavior stay consistent across machines.
2
Install Config. Drop ci.NuGet.Config beside the repo or under ~/.nuget/NuGet for the CI user; export NUGET_PACKAGES in the job shell first.
3
Turn on lock files. Enable RestorePackagesWithLockFile, run restore locally, commit packages.lock.json, then set RestoreLockedMode=true in CI.
4
Cap HTTP. Begin with maxHttpRequestsPerSource=8; watch SSD queue depth; step down to 4 before blaming the network.
5
Smoke restore. Run dotnet restore --verbosity minimal on a tiny project stage before solution-wide restores to warm TLS and caches.

⑥ Numbers you can cite in design docs

Eight concurrent requests per source is a practical ceiling for many shared Apple Silicon CI hosts in 2026; lower to four when restores coincide with heavy Git LFS or Docker layer pulls.

Locked restore in CI catches drift immediately; keep a scheduled unlocked restore on a maintainer branch weekly so feed outages do not block every PR.

One global packages folder per pool routinely beats per-job folders for hit rate, as long as compliance allows mixed public and private artifacts on the same volume.

⑦ FAQ: NuGet on remote Mac runners

Should globalPackagesFolder live on the system volume? Yes, when that volume is fast NVMe and sized for multi-tenant caches. Rotate old package directories during maintenance windows.

Does RestoreLockedMode replace CPM? No—they solve different problems. CPM centralizes versions; lock files pin graphs. Use both when you need reproducible CI and one place to bump dependencies.

Why does raising MaxHttpRequestsPerSource slow restores? Too many parallel small writes fragment SSD free space and inflate filesystem metadata work. Drop concurrency before adding job-level parallelism.

How do I refresh locks after a vulnerability patch? Run dotnet restore --force-evaluate (SDK-dependent) or temporarily disable locked mode on a branch, commit the new lock file, then re-enable CI enforcement.

Structured data note (HowTo / FAQPage)

This page already embeds BlogPosting, BreadcrumbList, HowTo, and FAQPage JSON-LD in <head>. When you fork the template, keep question text in FAQPage aligned verbatim with visible FAQ headings, and ensure HowTo steps reference only stable on-page anchors for reviewer trust.

Summary

Order feeds with packageSourceMapping, park packages on NVMe via globalPackagesFolder or NUGET_PACKAGES, cap MaxHttpRequestsPerSource, and enforce RestoreLockedMode once packages.lock.json is part of your review culture. Renting a remote Mac close to your approved mirrors turns those knobs into real wall-clock savings for dotnet restore and downstream test stages.

No-login next steps: homepage, pricing, purchase, help center, technical blog.

Colocate build agents with sane egress, lock your graphs, and treat restore concurrency as a disk budget—not a free multiplier.

Remote Mac for .NET restore & CI

Apple Silicon hosts with SSD-friendly cache layout and SSH access—ideal when you want faster NuGet restores without shipping hardware. Browse the homepage, purchase options, or help articles without signing in.