/commands/nuget/dotnet-pack-create-nuget-package
dotnet pack: create a NuGet package (.nupkg) from a project
- tool
- NuGet
- versions
- .NET 8, .NET 9, .NET 10
- os
- linux, macos, windows
dotnet pack -c Release -o nupkgs builds the project and writes a NuGet package, PackageId.Version.nupkg, into the nupkgs folder. The package id defaults to the project name and the version to 1.0.0; set PackageId, Version, Authors, Description and other metadata in the project file, or override the version per build with -p:Version=1.2.3. Upload the result with dotnet nuget push.
Pack a library
dotnet new classlib -o Acme.Text > /dev/null
cd Acme.Text
dotnet pack -c Release -o ../nupkgs -v q --nologo > /dev/null
dotnet pack -c Release -o ../nupkgs -v q --nologo -p:Version=1.2.3 > /dev/null
ls ../nupkgs
unzip -l ../nupkgs/Acme.Text.1.2.3.nupkg | grep -oE "lib/[^ ]+\.dll|Acme\.Text\.nuspec"Output
Acme.Text.1.0.0.nupkg Acme.Text.1.2.3.nupkg Acme.Text.nuspec lib/net10.0/Acme.Text.dll
The first run produced the default 1.0.0, the second used the version passed on the command line. A .nupkg is a zip file, so unzip -l lists it: it contains the compiled DLL under lib/TARGET-FRAMEWORK/ and a generated .nuspec manifest with the metadata.
Metadata in the project file
<PropertyGroup>
<TargetFramework>net8.0</TargetFramework>
<PackageId>Acme.Text</PackageId>
<Version>1.2.3</Version>
<Authors>Acme</Authors>
<Description>Text helpers.</Description>
<PackageLicenseExpression>MIT</PackageLicenseExpression>
<PackageReadmeFile>README.md</PackageReadmeFile>
<RepositoryUrl>https://github.com/acme/text</RepositoryUrl>
</PropertyGroup>
<ItemGroup>
<None Include="README.md" Pack="true" PackagePath="\" />
</ItemGroup>Options
| Command | What it does |
|---|---|
| dotnet pack | Release build (the default since .NET 8), package into bin/Release |
| dotnet pack -c Release -o nupkgs | Release build into the nupkgs folder |
| dotnet pack -p:Version=1.2.3 | Override the package version |
| dotnet pack --version-suffix beta.1 | With VersionPrefix 1.2.3 in the project, produce 1.2.3-beta.1 |
| dotnet pack --include-symbols -p:SymbolPackageFormat=snupkg | Also create a .snupkg symbol package |
| dotnet pack --no-build | Package an existing build (after dotnet build -c Release) |
| dotnet pack Shop.slnx | Every packable project of a solution |
| dotnet nuget push nupkgs/*.nupkg -s SOURCE | Upload packages to a feed |
Pitfalls
- Same version twice. NuGet caches packages by id and version. Re-packing 1.0.0 with different code and installing it again on the same machine gives the old cached copy; bump the version or clear the cache with dotnet nuget locals.
- Missing metadata warnings. Without
Authors,Descriptionor a license, pack still succeeds but prints NuGet pack warnings (NU5xxx codes); NuGet error codes explains the families. - Application projects. Packing a console app creates a library-style package unless you set
PackAsTool(for a .NET tool); for programs,dotnet publishis usually what you want. --version-suffixneedsVersionPrefix. If the project setsVersiondirectly, the suffix is ignored.
How to confirm it worked
ls nupkgs shows the .nupkg file with the expected id and version. Install it in another project from the local folder with dotnet add package Acme.Text --version 1.2.3 -s ../nupkgs and build that project.
FAQ
How do I test a package before publishing it?
Add the output folder as a local source, for example dotnet nuget add source ./nupkgs -n local, or pass -s ./nupkgs to dotnet add package in another project. NuGet caches packages by id and version, so bump the version (or clear the cache) after changing it.
How do I publish the package to nuget.org?
Use dotnet nuget push file.nupkg --source https://api.nuget.org/v3/index.json with an API key from your nuget.org account. Published versions cannot be overwritten, only unlisted.
Why does dotnet pack skip my test project?
Test project templates set IsPackable to false, so dotnet pack on a solution packs only the libraries. Set IsPackable to true in a project to include it.