How to Add a Private NuGet Feed
Authenticate and consume a private feed from the CLI, Visual Studio, and your CI pipeline.
Public packages on nuget.org install with no setup. Private packages are different: the feed requires authentication, so before dotnet restore will work, you have to tell NuGet where the feed is and how to log in.
This guide shows every common way to do that, from a one-line CLI command to a shared nuget.config, Visual Studio, and CI/CD pipelines, with a focus on keeping your credentials out of source control.
1. What a private NuGet feed is
A private NuGet feed is a package source that only authorized users can read. Teams use them to share internal libraries, and vendors use them to distribute commercial packages to paying customers. Unlike nuget.org, the feed sits behind authentication, so every machine that restores from it needs credentials.
The good news is that NuGet has supported authenticated sources for years. You do not need special tooling, just the right entries in your configuration.
2. The two pieces you need
Whatever method you pick, you are always supplying the same two things:
- The feed URL, which ends in /index.json for a NuGet v3 feed. For pkgstore it looks like https://www.pkgstore.io/api/nuget/acme/toolkit/index.json.
- Credentials, a username and password sent as basic authentication.
With a managed platform, you get both from a dashboard. With pkgstore, subscribing or accepting a free access grant provisions your credential automatically.
3. Method A: the dotnet CLI
The fastest way to add a source on your own machine is a single command:
dotnet nuget add source \
https://www.pkgstore.io/api/nuget/acme/toolkit/index.json \
--name acme-toolkit \
--username [email protected] \
--password YOUR_CREDENTIAL \
--store-password-in-clear-text
On Linux and macOS, NuGet cannot encrypt the stored password, so --store-password-in-clear-text is required. That writes the password to your user-level config in plaintext, which is fine for a personal dev machine but not for anything shared. For those cases, use the next two methods.
4. Method B: nuget.config
A nuget.config committed next to your solution gives everyone the same source automatically. The trick is to reference the secret through an environment variable rather than hard-coding it:
<?xml version="1.0" encoding="utf-8"?>
<configuration>
<packageSources>
<add key="acme-toolkit"
value="https://www.pkgstore.io/api/nuget/acme/toolkit/index.json" />
</packageSources>
<packageSourceCredentials>
<acme-toolkit>
<add key="Username" value="%PKG_USER%" />
<add key="ClearTextPassword" value="%PKG_PASS%" />
</acme-toolkit>
</packageSourceCredentials>
</configuration>
NuGet expands %PKG_USER% and %PKG_PASS% from the environment at restore time. The config is safe to commit because it contains no secret, only the variable names. The element under packageSourceCredentials must match your source key exactly.
Never commit plaintext credentials. If you must, add nuget.config to .gitignore, but the environment-variable pattern above lets you commit the config and keep the secret out of history.
5. Method C: Visual Studio
If you prefer the UI:
- Open Tools → Options → NuGet Package Manager → Package Sources.
- Click +, set a name, paste the feed URL, and click Update.
- The next restore prompts for credentials. Enter your username and password, and Visual Studio saves them in your user credential store.
This is convenient for a single developer, but it does not help your build server, which brings us to CI/CD.
6. Method D: CI/CD pipelines
In CI, never paste credentials into the pipeline file. Store them as encrypted secrets and expose them as environment variables that your committed nuget.config reads. For GitHub Actions, that looks like this:
- name: Restore
run: dotnet restore
env:
PKG_USER: ${{ secrets.PKG_USER }}
PKG_PASS: ${{ secrets.PKG_PASS }}
The same idea applies to any provider: define the secret in the pipeline settings, map it to PKG_USER and PKG_PASS, and let your nuget.config do the rest. Because the variables are injected at runtime, nothing sensitive ever lands in your repository.
7. Restoring and verifying
With the source configured, restore and add packages as usual:
dotnet restore
dotnet add package Acme.Toolkit
Your private feed sits alongside nuget.org, so public dependencies keep resolving from the public gallery while your private packages come from the authenticated source.
8. Troubleshooting
- 401 Unauthorized: the username or password is wrong, or the environment variables did not expand. Echo them in your shell to confirm they are set.
- 403 Forbidden: you authenticated, but your access is not active. On pkgstore this usually means the subscription was cancelled or suspended.
- Old credentials keep being used: clear the cache with dotnet nuget locals http-cache --clear, then restore again.
- Source not found: make sure the URL ends in /index.json and the feed name matches the credentials section.
For the step-by-step reference version of this, see the docs: Consuming a feed and Managing access.
Distributing your own packages?
pkgstore provisions feed credentials automatically when customers subscribe, and revokes them when they cancel. No manual credential juggling, no payment integration to build.
Get started free