Introduction

NuGet API keys have traditionally been a convenient way to authenticate package publishing from local machines and CI/CD pipelines. The problem is that a credential used by an automated pipeline can remain valid long after the pipeline needs it. If that credential is exposed through logs, configuration files, build artifacts, or a compromised runner, the attacker may be able to use it to publish or manage packages within its configured scope.

NuGet already provides scoped API keys with expiration periods, and current NuGet guidance also recommends more secure approaches such as environment variables, credential providers, and Trusted Publishing where available.

The important change for teams is not simply creating another API key. It is treating package-publishing credentials as short-lived deployment credentials and designing the pipeline around rotation.

This article explains how to migrate a .NET CI/CD pipeline from a long-lived NuGet API key to a 30-day credential model, how to avoid common migration failures, and when keyless Trusted Publishing is a better option.

Why Long-Lived NuGet API Keys Are a CI/CD Risk

A NuGet API key should be treated like a password. Microsoft explicitly recommends keeping it secret and deleting or regenerating it if it is exposed. Scoped keys can also be restricted by operation and package pattern.

A long-lived credential creates a larger security window.

For example, consider a pipeline that stores this secret:

NUGET_API_KEY = <long-lived-secret>

If the secret is accidentally exposed today but nobody notices it for several months, the attacker may still be able to use it.

With a 30-day credential, the exposure window is substantially shorter.

The security principle is straightforward:

Long-lived credential
        |
        v
Long exposure window
        |
        v
Higher impact if leaked

versus:

Short-lived credential
        |
        v
Smaller exposure window
        |
        v
Lower credential lifetime risk

Expiration does not replace least privilege, however. A 30-day key with permission to push every package is still broader than necessary.

What Changes When You Move to 30-Day Credentials

The main operational change is that credential rotation becomes part of the deployment process.

A useful migration model is:

AreaLong-lived key30-day key
Credential lifetimeLongShort
RotationOccasional/manualPlanned/regular
Exposure windowLargerSmaller
CI/CD secret updateInfrequentRecurring
Package scopeShould be restrictedShould be restricted
Operational dependencyLowHigher
Security postureWeakerStronger
Keyless alternativeUsually separate migrationCan be replaced by Trusted Publishing

NuGet API keys support scopes for operations and package patterns, allowing teams to create separate credentials for different publishing responsibilities.

That means a production publishing key should not automatically be treated as a universal organization-wide credential.

Step 1: Audit Existing NuGet Credentials

Before creating replacement keys, identify where the existing credential is used.

Search your CI/CD configuration for:

NUGET_API_KEY
NUGET_AUTH_TOKEN
NuGetApiKey
dotnet nuget push
nuget push

Also inspect:

The goal is to establish a credential inventory.

For example:

Repository A
  -> Package A
  -> GitHub Actions
  -> NUGET_API_KEY

Repository B
  -> Package B
  -> Azure pipeline
  -> NuGet API key

Repository C
  -> Multiple packages
  -> Shared publishing credential

A shared credential is an important finding because rotating it can affect multiple pipelines simultaneously.

Step 2: Create a Scoped Replacement Key

Create a new API key rather than modifying the existing production pipeline first.

The key should be scoped to the smallest practical set of permissions.

For example:

Scope:
  Push

Package pattern:
  Contoso.*

Expiration:
  30 days

The exact package pattern should match your publishing model.

If a pipeline publishes only one package, there is little reason to give it access to unrelated packages.

This follows the principle of least privilege:

Required permission
        |
        v
Required package scope
        |
        v
Required lifetime

NuGet documentation confirms that API keys can be scoped by operation and package glob pattern and can have an expiration timeframe.

Step 3: Store the Credential Outside Source Control

Never put the API key directly into a repository.

Avoid:

env:
  NUGET_API_KEY: "actual-secret-value"

Instead, reference the CI/CD platform's secret store:

env:
  NUGET_API_KEY: ${{ secrets.NUGET_API_KEY }}

The exact syntax depends on your CI/CD platform.

Current NuGet tooling supports the NUGET_API_KEY environment variable for package publishing. Microsoft documents this capability for NuGet 7.6 and later.

This approach also avoids placing the API key directly into the command-line arguments.

Step 4: Update the Package Publishing Command

A typical publishing command is:

dotnet nuget push ./artifacts/MyLibrary.1.0.0.nupkg \
  --source https://api.nuget.org/v3/index.json

When NUGET_API_KEY is supplied through the environment, the command does not need to contain the secret explicitly.

For example:

export NUGET_API_KEY="$NUGET_API_KEY"

dotnet nuget push ./artifacts/MyLibrary.1.0.0.nupkg \
  --source https://api.nuget.org/v3/index.json

The NuGet CLI documentation states that NUGET_API_KEY can provide the API key for push operations, while an explicit --api-key command-line value takes precedence.

This precedence is worth remembering during migration. A pipeline can appear to use the new secret while an older command-line credential is still taking precedence.

Step 5: Test the New Credential Before Revoking the Old One

Do not immediately delete the old key after generating the replacement.

First execute a controlled publishing test.

A practical sequence is:

  1. Create the new scoped credential.

  2. Store it in the CI/CD secret manager.

  3. Update the pipeline.

  4. Run the pipeline against the intended package.

  5. Confirm the package is published successfully.

  6. Confirm no unexpected packages can be modified.

  7. Check logs for accidental secret exposure.

  8. Revoke the old credential.

This creates a safer migration path.

If the new key fails, the existing credential remains available while the problem is investigated.

Step 6: Rotate the Credential Without Creating Pipeline Downtime

The biggest operational mistake is treating expiration as the rotation process.

Suppose a credential expires at:

Day 30, 10:00 UTC

and the team discovers the problem at:

Day 30, 10:15 UTC

The next package release may fail.

A better model is:

Day 1
  |
  +-- Create credential
  |
  v
Day 20
  |
  +-- Prepare replacement
  |
  v
Day 21
  |
  +-- Update CI/CD secret
  |
  v
Day 21+
  |
  +-- Validate publishing
  |
  v
Before expiry
  |
  +-- Revoke old credential

The exact rotation schedule should be determined by the credential's actual expiration date and the organization's release process.

Do not assume that a 30-day credential means you should wait until day 30.

A Safer CI/CD Pattern

A production-oriented pipeline should separate package creation from package publishing.

For example:

steps:
  - name: Restore
    run: dotnet restore

  - name: Build
    run: dotnet build --configuration Release --no-restore

  - name: Test
    run: dotnet test --configuration Release --no-build

  - name: Pack
    run: dotnet pack --configuration Release --no-build --output ./artifacts

  - name: Publish
    env:
      NUGET_API_KEY: ${{ secrets.NUGET_API_KEY }}
    run: |
      dotnet nuget push ./artifacts/*.nupkg \
        --source https://api.nuget.org/v3/index.json

This structure has an important security property: the secret is introduced only at the publishing stage.

Build and test jobs do not need access to the publishing credential.

That reduces the number of places where a secret can potentially be exposed.

When Trusted Publishing Is Better Than API Keys

Short-lived API keys are not necessarily the final destination.

NuGet supports Trusted Publishing, which uses short-lived credentials issued through a CI/CD identity mechanism rather than requiring a long-lived API key to be stored as a persistent secret. Microsoft documents GitHub Actions support using OIDC-based authentication, with NuGet issuing a temporary API key for the publishing operation.

The conceptual flow is:

CI/CD workflow
      |
      v
OIDC identity token
      |
      v
NuGet validates trusted publisher
      |
      v
Temporary publishing credential
      |
      v
Package push

The temporary NuGet credential described by Microsoft is valid for one hour, making it significantly different from manually managed API keys.

For supported CI/CD environments, Trusted Publishing can eliminate the recurring secret-rotation problem altogether.

30-Day API Keys vs Trusted Publishing

Capability30-Day API KeyTrusted Publishing
Stored secret requiredUsually yesNo persistent API key
Rotation workRequiredGreatly reduced
Credential lifetimeConfigured expirationShort-lived
CI/CD identitySecret-basedIdentity-based
Least privilegeAPI key scopesTrusted publisher policy
Setup complexityLowerHigher initially
Long-term directionUseful migration optionStronger keyless model

For teams that cannot immediately adopt Trusted Publishing, short-lived scoped keys are still a meaningful improvement.

Common Migration Mistakes

Putting the New Key in the Repository

This defeats the purpose of credential rotation.

Use the CI/CD platform's secret management mechanism instead.

Giving the Key Excessive Package Scope

A pipeline publishing one package should not automatically receive unrestricted publishing access.

Use the narrowest package pattern and operation scope practical.

Rotating Without Testing

Changing a credential and waiting for the next production release to discover a configuration problem is risky.

Perform a controlled publishing test first.

Leaving the Old Key Active

After migration, the old credential becomes unnecessary attack surface.

Once the replacement has been verified, revoke the old key.

Forgetting Other Pipelines

Shared credentials are particularly easy to miss.

Search all repositories and deployment systems before revocation.

Logging Secrets

Avoid diagnostic commands that print environment variables or authentication configuration.

Also review CI/CD logs after migration to make sure the credential is not being exposed.

Troubleshooting NuGet Authentication Failures

A publishing command can fail for several reasons.

SymptomLikely causeWhat to check
HTTP 403Invalid, expired, or unauthorized keyKey value, expiration, scope
Package cannot be pushedPackage scope mismatchGlob pattern
Pipeline still uses old credentialPrecedence issue--api-key, environment, config
Local publish works but CI failsSecret not available to runnerCI/CD secret configuration
New key works locally but not in CIEnvironment mismatchSecret name and job permissions
Credential appears ignoredWrong environment variableNUGET_API_KEY naming
Publish suddenly fails after several weeksCredential expiredExpiration date

NuGet documentation specifically identifies invalid or expired API keys and insufficient permissions as possible causes of HTTP 403 responses during publishing.

Also remember that NuGet credential sources have precedence rules. For authenticated feeds, NuGet can obtain credentials from environment variables, configuration files, or credential providers depending on the source and setup.

Best Practices for NuGet CI/CD Credential Security

A practical baseline is:

  1. Use scoped API keys rather than broad credentials.

  2. Set a short expiration period.

  3. Store credentials in the CI/CD secret manager.

  4. Introduce secrets only in the publishing job.

  5. Never commit API keys to source control.

  6. Avoid printing environment variables in pipeline logs.

  7. Test replacement credentials before revoking existing credentials.

  8. Track expiration dates.

  9. Remove credentials that are no longer required.

  10. Evaluate Trusted Publishing for supported workflows.

The important point is that credential security is both a technical and operational problem. A secure credential that nobody knows how to rotate is eventually an operational risk.

Frequently Asked Questions

Should I use one NuGet API key for all repositories?

Prefer separate, narrowly scoped credentials where practical. A shared credential increases the impact of a compromise and makes rotation more disruptive.

Can I pass the NuGet API key directly to dotnet nuget push?

Yes. The --api-key option is supported, but using the CI/CD environment variable mechanism can help avoid putting the secret directly into command arguments. NuGet documents NUGET_API_KEY as an alternative for push operations.

What happens when the API key expires?

Publishing operations using that credential fail authentication. The replacement credential must be configured before the next release that depends on it.

Is a 30-day key automatically secure?

No. Shorter lifetime reduces exposure, but you still need least privilege, secure storage, logging controls, and proper rotation.

Should I migrate directly to Trusted Publishing?

If your CI/CD environment and NuGet publishing workflow support it, Trusted Publishing is worth evaluating because it removes the need to maintain a persistent publishing API key.

Conclusion

Moving NuGet publishing credentials to a short-lived model is more than a credential replacement exercise. It is an opportunity to redesign the publishing pipeline around least privilege, controlled secret exposure, predictable rotation, and eventually keyless authentication.

For teams that still depend on API keys, a scoped 30-day credential provides a practical transition: create the replacement, store it securely, test the publishing workflow, rotate before expiration, and revoke the old key. For supported CI/CD environments, Trusted Publishing provides an even stronger model by replacing persistent secrets with short-lived workload identity.

The safest CI/CD pipeline is not the one with the most complicated security configuration. It is the one where credentials have the minimum permissions, exist for the minimum practical time, and are automatically or predictably replaced before they become a release-time problem.