user

How to Use CI/CD Pipelines for Your NuGet Packages

Introduction

Eric Seng

Eric Seng


LATEST POSTS

Best Practices for Versioning NuGet Packages at Scale 30th December, 2025

NuGet in the Enterprise 11th April, 2024

NuGet

How to Use CI/CD Pipelines for Your NuGet Packages

Posted on .

This article is part of our series on NuGet at Scale, also available as a chapter in our free, downloadable eBook.

I totally get it. CI/CD for NuGet isn’t just annoying—it can feel straight-up impossible. Many teams get frustrated using CI/CD for their NuGet pipelines since it doesn’t play nice with the best practices they’ve already implemented for their application pipelines.

CI/CD is supposed to help your team produce, test, and deliver NuGet packages faster and better. Still, a lot of teams skip it entirely. Why? Because they’re flooded with a ton of builds, packages buried in packages (most of which no one will ever use), and it all just feels like it doesn’t actually work.

In this article, we’ll explore why and how to implement CI/CD with your NuGet packages and how to implement a solution that works at scale. We’ll cover the commonly encountered challenges, specifically the CI/CD and NuGet disconnect, along with the non-options for handling these issues, and the secret solution to reconciling CI/CD and NuGet in your team’s pipeline.

Challenges with CI/CD & NuGet

Teams new to building/using their own NuGet packages with CI/CD usually hit the same two roadblocks:

💥 You end up drowning in packages—most of which no one will ever actually use.

💥 You either can’t use approved/tested pre-release packages in production… or your team just starts using pre-release packages anyway.

Without CI/CD, you’d simply create a NuGet package when it’s ready to ship, but with CI/CD, suddenly you’re swamped in dozens of packages, frequent library updates, more builds, and the difficulty of keeping track of all the version numbers that come with this.

The real disconnect? It happens when teams try to treat NuGet pipelines like application pipelines. Sure, the benefits and goals are the samemore stable, reliable releases—but applying application CI/CD best practices to NuGet will almost always lead to developer headaches.

✔ CI/CD for Applications

It’s pretty straightforward: a build artifact should only hit production after it’s been tested in a number of environments.

⚠ CI/CD for NuGet Packages?

This is where a lot of frustration kicks in: a NuGet package is essentially unusable until it reaches the last stage of the pipeline—Publishing.

The CI/CD and NuGet Disconnect

This frustration really comes down to three best practices your team is following:

  1. Packages are Immutable (Read-only). Once published, a NuGet package file can’t be modified. You can’t “edit” the version number of a package or change its status, since the version number is part of the metadata baked into the file itself.
  2. Untested code shouldn’t be deployed. Rebuilding a NuGet package can produce different results, especially when you throw wildcard dependencies into the mix. This means you have to test the exact code you’ve just built, if you’re planning to ship.
  3. Only deploy stable (non-pre-release) packages. It’s right there in the name—pre-release packages don’t belong in a production environment. Only stable versions should ever be released in your applications.

Three Non-options for NuGet CI/CD

Once a team kicks off CI/CD for their private NuGet packages, they’re in for a ton of builds and a whole bunch of packages that’ll never actually get used. Most teams see only three solutions to choose from, and honestly, none of them are great.

✗Use new Version Numbers at build time

How: With every new build, you create a brand-new version. That could mean using three-digit versioning (e.g. 3.4.2, 3.4.3, 3.4.112), or four-digit versioning (e.g. 3.4.0.0, 3.4.0.1, 3.4.0.112) for every new unstable package version.

😊 It’s super clear which version is the latest.

😓 But neither approach follows SemVer, and neither indicates package stability. Further, taking the three-digit versioning route means sacrificing an entire number (major, minor, or patch) to communicate the pre-release version.

✗Overwrite packages when you publish

How: Download your package (e.g. 3.4.2), overwrite it every time you build, and then re-upload it. 

😊 Your team avoids a landslide of packages and builds.

😓 This totally breaks the immutability rule. If multiple “versions” of “version 3.4.0” exist, how can you tell which one’s stable? Plus, overwriting creates caching headaches, since Visual Studio (and CI servers) generally won’t download a package that’s already downloaded. That means your team members have to clear the package caches to use the most recent v3.4.0.

✗Deploy prerelease packages

How: Add pre-release labels to the end of your package (e.g. -ci.1, -ci.2, ci.11). A package is tested in your application, and once it passes, it’s good to go for production.

😊 The package’s quality is clearly labeled, and you can apply all your standard CI/CD best practices to this pipeline. This is the best way to go.

😓 Here’s the catch: since your team is following CI/CD best practices, you’re not using pre-release packages in production. Now, you have to create a brand new stable version of the package, sending it through the pipeline all over again. Hello, CI/CD loop.

It’s here where many teams start to feel overwhelmed, caught between wasting time creating multiple packages that will never be consumed, or throwing SemVer out the window.

✔ The Secret to CI/CD for NuGet

Sure, it seems difficult to reconcile package immutability, SemVer, and continuous integration for your private NuGet packages, but using “repackaging” will let you use these best practices without the headache.

Repackaging creates a new package from an existing package, using the exact same content—just with a different name. For example, let’s say build 8 yields 1.0.0-ci.8 for testing; once approved and repackaged, a release candidate, 1.0.0-rc.8, is created. Finally, a stable version, 1.0.0, will be created for deployment in production applications.

How to Repackage a NuGet Package

A NuGet package is really just a zip file with a .nupkg extension, which means repackaging takes just a few simple steps:

  1. Downloading the package.
  2. Opening the package as a zip file.
  3. Editing the .nuspec manifest file and changing the version number.
  4. Publishing the package file.

That said, this is definitely not something you want to keep doing by hand. You can script your CI/CD tool to include repackaging in the pipeline (our BuildMaster can do this), or you can use a package server with built-in repackaging functionality, like ProGet.

NuGet CI/CD Done Right

CI/CD for NuGet packages can feel nigh impossible when your team follows application CI/CD best practices. A tirade of builds, redundant packages, and unstable packages often leads development teams to skip CI/CD, even though it should be benefiting productivity.

However, by adapting repackaging into your CI/CD pipeline, you can ensure that what goes to production is exactly what you have tested, alleviating the worry of wildcard builds and unstable packages, while steering your team clear of an avalanche of unused NuGet packages.

This will all be a lot to process, so be sure to bookmark this article for future reference! Or, even better, check out our eBook, “NuGet at Scale”. It has everything here, plus it’s filled with tips on debugging with Symbols and Source Link, using lock files for repeatable builds, and much more! Get your free copy now!

Eric Seng

Eric Seng

Navigation