Package Sources and Source Mapping

This section documents MSBuild 18.9, the .NET SDK 10.0.402 (with its dotnet CLI) and NuGet 7.9, as published in the MSBuild, .NET and NuGet documentation on learn.microsoft.com, which is the reference these pages are written and verified against. Material about .NET 11 / NuGet 8.0 is still in preview and is always flagged as such — never presented as baseline.

This content was generated with the assistance of AI and should be verified against the official documentation before being relied on in production.

The C# Reference bibliography lists the reference material consulted while preparing these pages.

A package source is anywhere NuGet can find packages: nuget.org, a folder, a file share, or an HTTP feed such as Azure Artifacts, GitHub Packages or a self-hosted BaGetter. With more than one source configured, package source mapping decides which source may supply each package ID — the defence against a public package impersonating a private one. This page uses NuGet 7.9.0 (.NET SDK 10.0.402), a folder feed and a BaGetter instance on localhost:5555. Configuration files themselves are covered on nuget.config; hosting a feed on Private Feeds.

Kinds of sources

  • nuget.org — https://api.nuget.org/v3/index.json, the public registry.

  • Local folders and file shares — a path. NuGet reads two layouts: flat (all .nupkg files in one folder) and hierarchical (<id>/<version>/<id>.<version>.nupkg plus the .nuspec and hash), which is faster for large feeds. nuget.exe init and nuget.exe add create the hierarchical layout. dotnet nuget push to a folder produced a flat one — and failed when the folder did not exist yet:

    Pushing Ledgerline.Money.1.5.0.nupkg to '~/srcs/hfeed'...
    error: Could not find a part of the path '~/srcs/hfeed/Ledgerline.Money.1.5.0.nupkg'.

    After mkdir hfeed the same command printed Your package was pushed. and the folder held hfeed/Ledgerline.Money.1.5.0.nupkg.

  • HTTP feeds — a V3 service index URL (NuGet Server API).

HTTPS is required

A source that is plain http:// is an error since NuGet 6.12 (.NET 9 SDK):

~/srcs/P/P.csproj : error NU1302: You are running the 'restore' operation with an 'HTTP' source: http://localhost:5555/v3/index.json. NuGet requires HTTPS sources. To use an HTTP source, you must explicitly set 'allowInsecureConnections' to true in your NuGet.Config file. Refer to https://aka.ms/nuget-https-everywhere for more information.

The opt-out is per source:

<add key="bagetter" value="http://localhost:5555/v3/index.json" allowInsecureConnections="true" />

after which the restore succeeded. It is acceptable for a feed on localhost; anywhere else, plain HTTP lets anyone on the network replace packages and read credentials.

The rollout, per the documentation: NU1803 warnings from NuGet.exe 6.3, Visual Studio 17.3 and ".NET 6.0.100", and NU1302 errors from 6.12, 17.12 and .NET 9.0.100. The middle entry is inconsistent: SDK 6.0.100 shipped NuGet 6.0, not 6.3 (6.3 came with SDK 6.0.400). Likewise, the dotnet nuget add source page says --allow-insecure-connections is "available since .NET 9 SDK", while the NuGet 6.11 release notes place it in SDK 8.0.400.

Managing sources from the CLI

dotnet nuget add source ~/srcs/hfeed --name folder --configfile nuget.config
dotnet nuget add source https://api.nuget.org/v3/index.json --name nuget.org --configfile nuget.config
dotnet nuget disable source nuget.org --configfile nuget.config
dotnet nuget list source --configfile nuget.config
dotnet nuget enable source nuget.org --configfile nuget.config
Package source with Name: folder added successfully.
Package source with Name: nuget.org added successfully.
Package source with Name: nuget.org disabled successfully.
Registered Sources:
  1.  bagetter [Enabled]
      http://localhost:5555/v3/index.json
  2.  folder [Enabled]
      ~/srcs/hfeed
  3.  nuget.org [Disabled]
      https://api.nuget.org/v3/index.json
Package source with Name: nuget.org enabled successfully.

update source changes a URL or credentials (--username, --password, --store-password-in-clear-text, --valid-authentication-types); remove source deletes one. Without --configfile the commands edit the user file. Disabling writes a disabledPackageSources entry rather than removing the source.

Package source mapping

Without mapping, NuGet asks every source for every package and takes the first answer that satisfies the version — so a package named like an internal one, published on nuget.org with a higher version, can win ("dependency confusion"). With a packageSourceMapping section, each package ID may only come from the sources whose patterns match it:

<packageSourceMapping>
  <packageSource key="nuget.org">
    <package pattern="*" />
  </packageSource>
  <packageSource key="bagetter">
    <package pattern="Ledgerline.*" />
    <package pattern="Ledgerline" />
  </packageSource>
  <packageSource key="folder">
    <package pattern="Ledgerline.Money" />
  </packageSource>
</packageSourceMapping>
Installed Ledgerline 1.0.0 from http://localhost:5555/v3/index.json to ~/srcs/gp/ledgerline/1.0.0 with content hash ImUylbiSkzpcLFJdtr0M…
Installed Ledgerline.Money 1.5.0 from ~/srcs/hfeed to ~/srcs/gp/ledgerline.money/1.5.0 with content hash ikqFv5yDeM68b02nB3Ou…
Installed Humanizer.Core 2.14.1 from https://api.nuget.org/v3/index.json to ~/srcs/gp/humanizer.core/2.14.1 with content hash lQKvtaTDOXnoVJ20ibTu…
flowchart LR Id{"package ID"} -->|"Ledgerline.Money
(exact ID: most specific)"| F["folder"] Id -->|"Ledgerline, Ledgerline.*
(prefix)"| B["bagetter"] Id -->|"anything else
(* default)"| N["nuget.org"]

The rules:

  • Most specific wins — an exact ID beats a prefix (Ledgerline.*), a longer prefix beats a shorter one, and * is the lowest; so Ledgerline.Money came from the folder although Ledgerline.* also matched the BaGetter source.

  • * makes a default source; without one, a package that matches no pattern fails to restore.

  • A pattern is a package ID or a prefix ending in *.

  • An unmapped source is not used for restore, which is how Ledgerline's BaGetter source stays a push target only; NU1102 then reports "Versions from … were not considered" (Package Versions and Ranges).

  • The global-packages folder is checked first — a package already there is used without consulting the mapping, so security-sensitive repositories use their own globalPackagesFolder.

Onboarding an existing repository: map everything to its current source by hand, or generate the section with the NuGet.PackageSourceMapper tool; then restore with an empty global-packages folder to prove the mapping is complete.

Source mapping has limits: it filters downloads during restore, install and update, but not metadata queries — dotnet package add, dotnet package list --outdated/--deprecated/--vulnerable and the Visual Studio browse and update lookups still send package IDs to every configured source, including public ones. Internal package names can therefore leak to nuget.org in those queries.

Common mistakes

  • Several sources and no mapping — dependency confusion; CPM repositories also get NU1507.

  • allowInsecureConnections on a remote feed — move the feed to HTTPS instead.

  • Expecting mapping to apply to a warm cache — the global-packages folder short-circuits it.

  • --add-source in a mapped repository — refused by tool commands; map the source instead (.NET Tools).