dotnet pack

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.

dotnet pack builds a project and creates a NuGet package (.nupkg) from it, using MSBuild properties instead of a hand-written .nuspec. This page lists the command’s options with .NET SDK 10.0.402 and shows the two that surprise people. Authoring a good package — metadata, layout, symbols, validation — is the subject of the Publishing and Distributing Libraries group, starting with Creating Packages with dotnet pack.

Options

Option Effect

-c, --configuration

Release by default since .NET 8 (PackRelease); -c Debug packs a debug build.

-o, --output

The folder for the packages (default bin/<Configuration>).

--no-build, --no-restore

Pack existing build output. --no-build implies --no-restore.

--version <v> (.NET 10)

Set the package version.

--version-suffix <s>

Set $(VersionSuffix).

--include-symbols

Also create a symbol package (.snupkg with SymbolPackageFormat=snupkg, otherwise .symbols.nupkg).

--include-source

Create a .symbols.nupkg with the PDBs and the source files under src/.

-s, --serviceable

Set <serviceable>true</serviceable> in the .nuspec.

--nologo

Do not display the startup banner or the copyright message. SDK 10.0.402 accepts --nologo, but its dotnet pack --help lists the option as -nologo, --no-logo with [default: True], so it changes nothing there.

-r, --ucr, --artifacts-path, --interactive, --disable-build-servers, -v

As for dotnet build (Restore, Build and Clean).

Packing Ledgerline

dotnet pack src/Ledgerline -o pk
dotnet pack src/Ledgerline -o pk --no-build --version 1.1.0-beta.1
dotnet pack src/Ledgerline -o pk --no-build --version-suffix rc.1
dotnet pack src/Ledgerline -o pk --no-build --include-symbols -p:SymbolPackageFormat=snupkg
# 1. dotnet pack src/Ledgerline -o pk (the build lines before it omitted)
  Successfully created package '~/ledgerline/pk/Ledgerline.1.0.0.nupkg'.
# 2. --no-build --version 1.1.0-beta.1
  Successfully created package '~/ledgerline/pk/Ledgerline.1.1.0-beta.1.nupkg'.
# 3. --no-build --version-suffix rc.1 (printed nothing)
# 4. --no-build --include-symbols -p:SymbolPackageFormat=snupkg
  Successfully created package '~/ledgerline/pk/Ledgerline.1.0.0.nupkg'.
  Successfully created package '~/ledgerline/pk/Ledgerline.1.0.0.snupkg'.

The # lines mark which command printed what; they are not part of the output.

Two of the four results are not what the commands suggest:

  • --version-suffix did nothing. Ledgerline.csproj sets <Version>1.0.0</Version>, and VersionSuffix only takes part when the project sets VersionPrefix instead (Version is then $(VersionPrefix)-$(VersionSuffix)). The third command printed nothing at all: the version was still 1.0.0, so with -v d the log shows Skipping target "GenerateNuspec" because all output files are up-to-date, and the existing Ledgerline.1.0.0.nupkg was left as it was.

  • --version with --no-build versions the package, not the assemblies. Ledgerline.1.1.0-beta.1.nupkg has <version>1.1.0-beta.1</version> in its .nuspec, but its lib/net10.0/Ledgerline.dll still says 1.0.0.0 and informational version 1.0.0+e33226ff…, because it was built before. Pass the version to the build (dotnet build -p:Version=1.1.0-beta.1, then dotnet pack --no-build -p:Version=1.1.0-beta.1), or let dotnet pack build.

The .snupkg holds only the PDBs (lib/<tfm>/Ledgerline.pdb). --include-source instead created Ledgerline.1.0.0.symbols.nupkg with the PDBs and src/Ledgerline/*.cs, the legacy format that nuget.org does not accept as a symbol package (Symbols, Source Link and Deterministic Packages). -s added <serviceable>true</serviceable> to the .nuspec.

Versioning strategy is covered on Versioning and Prerelease Packages.

Packing without a project

dotnet pack also accepts a .nuspec file — useful for content-only packages that have nothing to compile:

<?xml version="1.0" encoding="utf-8"?>
<package xmlns="http://schemas.microsoft.com/packaging/2013/05/nuspec.xsd">
  <metadata>
    <id>Ledgerline.Samples</id>
    <version>1.0.0</version>
    <authors>Ledgerline contributors</authors>
    <description>Sample journal files for Ledgerline.</description>
    <license type="expression">MIT</license>
  </metadata>
  <files>
    <file src="content/*.csv" target="contentFiles/any/any/journals" />
  </files>
</package>
dotnet pack Ledgerline.Samples.nuspec -o out
[NuGet Manager] The package Ledgerline.Samples.1.0.0 is missing a readme. Go to https://aka.ms/nuget/authoring-best-practices/readme to learn why package readmes are important.
[NuGet Manager] Successfully created package 'out/Ledgerline.Samples.1.0.0.nupkg'.

A file-based app (dotnet pack balance.cs) packs as a .NET tool, because PackAsTool is on by default for file-based apps (Run and File-Based Apps).

Deterministic packs

From SDK 10.0.400, -p:Deterministic=true -p:DeterministicTimestamp=<time> makes two packs of the same build output byte-for-byte identical (ZIP entry times and the .psmdcp part). With SDK 10.0.402, exporting SOURCE_DATE_EPOCH alone was not enough; passing it as DeterministicTimestamp was. The experiment is on Reproducible and Deterministic Builds.

Common mistakes

  • --version-suffix on a project that sets Version — ignored without a warning.

  • --version with --no-build — package and assembly versions disagree.

  • --include-source for nuget.org symbols — use --include-symbols -p:SymbolPackageFormat=snupkg.

  • Packing Debug by habit — pack defaults to Release since .NET 8; do not override it.