Publishing and Distributing Libraries

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.

The NuGet group covers consuming packages; this group covers producing them: designing a library for reuse, describing it, packing, validating, signing, versioning and publishing it, and looking after it once it is public. Every example was run with .NET SDK 10.0.402 (MSBuild 18.9, NuGet 7.9.0) on Linux, against the Ledgerline scenario of this section. Packages were pushed only to a local folder feed and to a BaGetter 1.6.5 container on localhost:5555; pushes to nuget.org, GitHub Packages and Azure Artifacts, Trusted Publishing and signing with a real certificate are shown as "not run here" examples, checked against the documentation, with placeholder keys. Ledgerline is never pushed to a public feed.

The life of a release

flowchart LR D["Design
targets, dependencies"] --> M["Describe
metadata, README,
icon, licence"] M --> P["Build and pack
dotnet pack"] P --> V["Validate
package validation,
API compatibility"] V --> S["Sign
(optional)"] S --> L[("Local folder feed")] S --> T[("Test feed
BaGetter, int.nugettest.org")] T --> N[("nuget.org
Trusted Publishing")] N --> A["After release
unlist, deprecate,
vulnerabilities"]

A release starts long before dotnet pack: the target frameworks and dependency ranges chosen at design time decide who can consume the package, and the metadata decides whether anyone finds and trusts it. Packing turns a project into a .nupkg; validation compares it with the previous release; signing is optional on nuget.org, which adds its own repository signature. Pushing to a test feed first catches packaging mistakes before they become permanent, because a version on nuget.org can be unlisted but never replaced.

The tool chain

Step dotnet CLI Other tools

Pack

dotnet pack (the MSBuild Pack target)

msbuild -t:pack; nuget pack for .nuspec-only packages

Sign

dotnet nuget sign

nuget sign (Windows); Microsoft’s sign tool (beta)

Push

dotnet nuget push

nuget push; the nuget.org web upload; CI tasks

Authenticate to nuget.org

NuGet/login@v1 (Trusted Publishing) in GitHub Actions

scoped API keys

Unlist

dotnet nuget delete

the nuget.org package page; nuget delete

A minimal local release of Ledgerline is two commands:

dotnet pack src/Ledgerline -o artifacts/rel -p:Version=1.0.1 -p:IncludeSymbols=true -p:SymbolPackageFormat=snupkg
dotnet nuget push artifacts/rel/Ledgerline.1.0.1.nupkg -s ledgerline-bagetter -k "$BAGETTER_API_KEY"
Pushing Ledgerline.1.0.1.nupkg to 'http://localhost:5555/api/v2/package'...
  PUT http://localhost:5555/api/v2/package/
  Created http://localhost:5555/api/v2/package/ 86ms
Your package was pushed.
Pushing Ledgerline.1.0.1.snupkg to 'http://localhost:5555/api/v2/symbol'...
  PUT http://localhost:5555/api/v2/symbol/
  Created http://localhost:5555/api/v2/symbol/ 60ms
Your package was pushed.

dotnet nuget push found the .snupkg next to the .nupkg and pushed it to the feed’s symbol endpoint without being asked.

Where packages can go

  • nuget.org — the public registry, the default source of every client (Publishing to nuget.org).

  • GitHub Packages and Azure Artifacts — hosted private (or public) feeds tied to a repository or an Azure DevOps organization.

  • Self-hosted servers — BaGetter, NuGet.Server, or any server implementing the NuGet V3 API.

  • Folders and file shares — the simplest feed, with no server at all.

The last three are covered on Private Feeds.

Dated changes

Date / release Change

NuGet 4.3 (2017)

SemVer 2.0.0 package versions

NuGet 4.9 (2018)

.snupkg symbol packages and the nuget.org symbol server; embedded licence files and licence expressions (PackageLicenseExpression); PackageLicenseUrl deprecated (NU5125)

NuGet 5.0 (2019)

buildTransitive/

NuGet 5.3 / VS 16.3 (2019)

embedded icon (PackageIcon); PackageIconUrl deprecated (NU5048); package deprecation on nuget.org

NuGet 5.10 preview 2 (2021)

embedded README (PackageReadmeFile), per the .nuspec reference; the release notes don’t mention it

.NET 6 SDK (2021)

package validation; dotnet nuget sign

NuGet 6.4 (2022)

Central Package Management GA

.NET 8 SDK (2023)

Source Link built into the SDK; the commit hash in InformationalVersion; dotnet pack defaults to Release; Microsoft.DotNet.ApiCompat.Tool

NuGet 6.12 / .NET 9 (2024)

SHA-1 certificate fingerprints deprecated in the sign commands

.NET 10 SDK (2025-11-11)

RID-specific, self-contained and Native AOT .NET tools, the any RID, dnx; dotnet pack of a .nuspec or a file-based .cs; dotnet pack --version

2025-09-22

Trusted Publishing on nuget.org announced (GitHub Actions; GitLab later); the NuGet/login action

NuGet 7.3 (10.0.200)

deterministic file order inside packages

NuGet 7.6 (10.0.300)

pack aware of TFM aliases (NU5051)

NuGet 7.9 (10.0.400)

deterministic pack revisited: Deterministic, DeterministicTimestamp, SOURCE_DATE_EPOCH

2026-08-17

new nuget.org API keys expire after at most 30 days; the 365-day option is gone. Every key created before that date expires on 2026-11-01.

.NET 11 (preview)

NETSDK1235 (PackAsTool with a custom .nuspec); consumer-side minimum publish age

The API key change is announced on the .NET blog (2026-08-03); no Microsoft Learn page states it yet, and Scoped API keys still shows a 365-day key (nuget.org Accounts, Organizations and API Keys).

Common mistakes

  • Pushing to nuget.org first — a version is permanent; try a local or test feed first.

  • Treating the API key as long-lived — since 2026-08-17 it lasts 30 days at most; prefer Trusted Publishing.

  • Skipping package validation — a removed public member is a breaking change that consumers find at run time.