Private Feeds
|
This section documents MSBuild 18.9, the .NET SDK 10.0.402 (with its 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. |
Not every package belongs on nuget.org: internal libraries, prereleases for a few testers, forks with local patches, or
a mirror for builds that must not depend on the internet. NuGet talks to any feed that implements its V3 API, and
to plain folders. This page sets up the options Ledgerline could use: a folder feed and a BaGetter server
were run here with .NET SDK 10.0.402 (NuGet 7.9.0); GitHub Packages, Azure Artifacts and NuGet.Server
are "not run here", with placeholders. Consuming several feeds safely — source mapping and credentials — is on
Package Sources and Source Mapping and
Authenticated Feeds and Credential Providers.
Folder feeds
A folder (local or a file share) is a feed. dotnet nuget push to a folder path just copies the file; it uses the
hierarchical layout (<id>/<version>/<id>.<version>.nupkg, much faster to read) only if the folder already holds at
least one package laid out that way. Into an empty folder:
dotnet nuget push artifacts/rel/Ledgerline.1.0.1.nupkg -s ./folderfeed
dotnet nuget push artifacts/rel/Ledgerline.1.0.1.nupkg -s ./folderfeed
Pushing Ledgerline.1.0.1.nupkg to '.../folderfeed'...
Your package was pushed.
Pushing Ledgerline.1.0.1.nupkg to '.../folderfeed'...
Your package was pushed.
The result was a flat folder with Ledgerline.1.0.1.nupkg, and the second push silently overwrote it. Seeded with
one package folder copied from the global-packages folder (the documented trick), the next push went hierarchical,
with the .nupkg.sha512 and .nuspec beside it, and pushing the same version again failed:
ledgerline.money/1.0.0/.nupkg.metadata
ledgerline.money/1.0.0/ledgerline.money.1.0.0.nupkg
ledgerline.money/1.0.0/ledgerline.money.1.0.0.nupkg.sha512
ledgerline.money/1.0.0/ledgerline.money.nuspec
ledgerline.money/1.0.0/lib/netstandard2.0/_._
error: Package 'Ledgerline.Money.1.0.0' already exists at feed '.../folderfeed-h'.
nuget add <nupkg> -source <folder> (nuget.exe) always writes the hierarchical layout, and nuget init <src> <dest>
converts a flat folder of packages into one. Folder feeds have no authentication beyond file permissions, no search
index and no vulnerability data — configure auditSources for NuGetAudit.
Self-hosted servers
BaGetter (third-party, open source) is a small NuGet V3 server with a database and file storage; version 1.6.5 ran here in Docker:
docker run -d --name bagetter -p 5555:8080 \
-e ApiKey="$BAGETTER_API_KEY" \
-e Storage__Type=FileSystem -e Storage__Path=/data \
-e Database__Type=Sqlite -e "Database__ConnectionString=Data Source=/data/db/bagetter.db" \
-e Search__Type=Database \
bagetter/bagetter:1.6.5
Its service index offers publish, symbol publish, search, autocomplete, registrations and the package base address — enough for push, restore and search, but no vulnerability data
(NuGet Server API). Pushes with a wrong key got
401 (Unauthorized), duplicates 409 (Conflict); dotnet nuget delete unlisted a version
(Managing Published Packages).
Behind HTTP it needs allowInsecureConnections="true" in nuget.config, acceptable only on localhost — anywhere else,
put it behind HTTPS (NU1302).
NuGet.Server 3.4.3 (.NET Foundation) is an older option: an ASP.NET (.NET Framework) web application that serves a
folder over the V2 (OData) protocol, on IIS (Windows). Push goes to /nuget (and /api/v2/package), with an
apiKey in web.config; it restructures the folder into the hierarchical layout on first run. Not run here.
GitHub Packages (not run here)
The NuGet registry of GitHub Packages lives at https://nuget.pkg.github.com/NAMESPACE/index.json, where NAMESPACE
is the user or organization. Authentication:
-
In GitHub Actions:
GITHUB_TOKENpublishes packages connected to the workflow’s repository, and reads packages the repository was granted access to. -
Elsewhere: a personal access token (classic) — fine-grained tokens are not supported — with
read:packagesto install andwrite:packagesto publish.
dotnet nuget add source "https://nuget.pkg.github.com/ledgerline/index.json" --name github \
--username ledgerline-bot --password "$GITHUB_TOKEN" --store-password-in-clear-text
dotnet nuget push "artifacts/*.nupkg" --source github --api-key "$GITHUB_TOKEN"
(--store-password-in-clear-text because there is no encryption on Linux and macOS — run it on a CI runner, not into a
committed file.) A package with a RepositoryUrl pointing at a GitHub repository is connected to that repository
and inherits its permissions; several packages can share one repository that way.
A documented pitfall: with GitHub Packages and nuget.org both configured, restores of ordinary public packages
(Microsoft.Extensions.*) can fail intermittently with 403, because NuGet asks every source for every package and
a GitHub authentication hiccup stops the restore. The fix GitHub gives is package source mapping — only
Ledgerline.* goes to github:
<packageSourceMapping>
<packageSource key="nuget.org"><package pattern="*" /></packageSource>
<packageSource key="github"><package pattern="Ledgerline.*" /></packageSource>
</packageSourceMapping>
Azure Artifacts (not run here)
An Azure DevOps feed is scoped to a project or the whole organization, private or public, and can include upstream sources (nuget.org and others):
https://pkgs.dev.azure.com/<ORGANIZATION>/<PROJECT>/_packaging/<FEED>/nuget/v3/index.json (project-scoped)
https://pkgs.dev.azure.com/<ORGANIZATION>/_packaging/<FEED>/nuget/v3/index.json (organization-scoped)
-
Authentication — the Azure Artifacts Credential Provider (a NuGet plugin) on developer machines; in Azure Pipelines the
NuGetAuthenticate@1task; elsewhere a PAT added withdotnet nuget add source <url> --username <user> --password <PAT>. The push API key is ignored and can be any string (the docs useAZ). -
Permissions — pushing needs the Feed Publisher (Contributor) role; with less, the push fails with 403. A new feed gives the project’s Build Service Feed and Upstream Reader (Collaborator) only.
-
Immutable versions — a version can’t be overwritten or reused, even after it is deleted.
-
Upstream sources — searched in a fixed order (packages published to the feed, then packages already saved from upstreams, then each upstream in its listed order), unlike NuGet’s parallel queries. A package installed from an upstream by a Collaborator or higher is saved in the feed, so builds survive an outage of nuget.org. A version that exists upstream can’t be published to the feed unless that upstream is disabled first.
-
Views —
@Local(the default, where every published package lands),@Prereleaseand@Release; promoting a version to a view releases it to the consumers of that view.
dotnet nuget push --source "https://pkgs.dev.azure.com/ledgerline/Ledgerline/_packaging/ledgerline-internal/nuget/v3/index.json" \
--api-key AZ artifacts/Ledgerline.1.0.1.nupkg
Source: Azure Artifacts: Set up the credential provider, Create a feed, Connect to a feed, Publish packages, dotnet CLI: Same organization, Different organization, Publish fails: version exists, Publish fails with 403, Upstream sources: Why, Search order, Save packages, Override packages, Feed views.
Azure DevOps as a whole is covered in Azure — Developer Tools and DevOps.
Choosing
| Feed | Good for | Watch out for |
|---|---|---|
Folder |
Local testing; offline CI caches |
No auth, no search, no audit data; flat layout overwrites silently |
BaGetter |
A small team; rehearsing nuget.org pushes |
You run it: backups, HTTPS, upgrades |
GitHub Packages |
Repositories on GitHub; |
Classic PATs only outside Actions; the 403 without source mapping |
Azure Artifacts |
Azure DevOps organizations; upstream caching; views |
Credential provider setup; immutable versions |
Common mistakes
-
A private feed without source mapping — dependency confusion and the GitHub 403.
-
Credentials in a committed
nuget.config— use environment variables or the credential provider. -
Pushing to a flat folder twice — the first package is overwritten without a word.
-
allowInsecureConnectionson a real network — packages and credentials in clear text. -
Expecting to "fix" a version on Azure Artifacts by deleting it — the number stays taken.