Azure DevOps Private Nuget Feed with Docker Build

by Brad Jolicoeur
01/02/2019

I recently had to add a private NuGet package feed to my Azure DevOps build pipeline that creates a Docker container. It looks like a lot of folks are struggling to do the same thing so I put some notes together on how I was able to get it to work without adding secrets to my source control.

The challenge comes in when the docker build tries to execute a nuget restore. The private repository needs credentials and the build fails with NuGet restore error NU1102: Unable to find package. The most common solutions I found online included configuring a nuget.config with the credentials in it. This means putting the credentials in source control (not a good practice) or generating a nuget.config file as part of the build process from pipeline variables. The first option was a non-starter and hacking together a script to generate the nuget.config seemed overly complex to maintain.  With this knowledge, I set off to piece together a solution that did not require storing secrets in source control and would be easy to live with.

My solution is to perform a NuGet restore as a build step in the pipeline which copies the packages to the agent. I then copy the packages to the build container and set the nuget.config to use the folder on the build container as the source for the packages. This means my nuget.config does not contain any secrets and can be stored in source control alongside my Dockerfile. 

Below are detailed steps on implementing this solution.

1. Create private NuGet feed in Azure DevOps under the Artifacts section of your project

2. Add Nuget Tool Installer step to Build Pipeline to ensure that Nuget 4.7.1 is installed on agent

Nuget Tool Install

steps:

- task: NuGetToolInstaller@0

 displayName: 'Use NuGet 4.7.1'

 inputs:

 versionSpec: 4.7.1

3. Add NuGet restore step after the installer step and configure to use the private feed you configured in step 1.

Important: Under advanced set the Destination directory to 'NugetPackage' (this name needs to align with the one you use in the nuget.config file)

NuGet Restore

steps:

- task: NuGetCommand@2

 displayName: 'NuGet restore'

 inputs:

 vstsFeed: '{your feed here}'

 restoreDirectory: NugetPackage

4. Create a nuget.config file in the same folder as your Dockerfile that is configured to use the local package source named 'NugetPackage'

Note: In my example the .sln file, Dockerfile and nuget.config all live adjacent in the root folder of the solution. 

<?xml version="1.0" encoding="utf-8"?>
<configuration>
    <packageSources>
        <add key="local" value="NugetPackage" />
    </packageSources>
</configuration>

5. Update you docker file accordingly 

  • Copy the source folder that contains your nuget packages from step 3 to the build container.
  • Run a dotnet restore prior to the build
  • When running dotnet build set the --no-restore flag
FROM microsoft/dotnet:2.1-aspnetcore-runtime AS base
WORKDIR /app
FROM microsoft/dotnet:2.1-sdk AS build
WORKDIR /src

COPY . . <--copies the nuget packages to the build container

RUN dotnet restore  <--this restore will use the nuget.config

RUN dotnet build -c Release -o /app --no-restore

FROM build AS publish
RUN dotnet publish -c Release -o /app --no-restore

FROM base AS final
WORKDIR /app
COPY --from=publish /app .
ENTRYPOINT ["dotnet", "bradjolicoeur.web.dll"]  <--make sure you change to reference your app