In this post, I’m setting up a private NuGet repository using Sonatype Nexus Repository OSS.

I did this setup using a dedicated VM running Ubuntu 16.04 LTS. Some additional prerequisites:

  • Java is a requirement, so I installed the openjdk 8 JRE, which, although it's not officially supported by Sonatype, worked fine for me.
  • My username in that VM is already nexus.

First things first, I downloaded the tar.gz file from the Downloads page and unzip it under /opt, e.g.:

sudo chown nexus:nexus /opt
wget https://download.sonatype.com/nexus/3/latest-unix.tar.gz
mv latest-unix.tar.gz /opt/
tar -zxf latest-unix.tar.gz

This gives two folders under /opt:

drwxrwxr-x  9 nexus nexus      4096 Mar  9 07:36 nexus-3.9.0-01/
drwxrwxr-x  3 nexus nexus      4096 Mar  9 07:36 sonatype-work/

What I like to do is to create a symbolic link to the specific version, so that I can upgrade and rollback in a safe way without changing other configuration files:

ln -s nexus-3.9.0-01 nexus

which gives me this layout:

lrwxrwxrwx  1 nexus nexus        14 Mar  9 07:46 nexus -> nexus-3.9.0-01/
drwxrwxr-x  9 nexus nexus      4096 Mar  9 07:36 nexus-3.9.0-01/
drwxrwxr-x  3 nexus nexus      4096 Mar  9 07:36 sonatype-work/

The next step is to install Nexus as a systemd service. All I had to do was copy the example from the documentation with no changes at all (since my username is already nexus and my installation directory is effectively /opt/nexus).

At this point, it is possible to access the UI at port 8081 (assuming no firewall is blocking it) and login to Nexus with the default admin credentials which are admin and admin123.

After logging in, I did two things:

  • change the admin's password
  • disable anonymous access to the server (Security -> Anonymous -> Allow anonymous users to access the server)

On a side note, the UX/UI of Nexus has improved since version 2, but it’s still a bit old fashioned in my personal opinion.

Out of the box, Nexus has a ready to use NuGet repository, so I don’t have to configure anything there. The repository’s feed is available at http://your-nexus-server:8081/repository/nuget-hosted/.

In order to publish to this NuGet repository, we need to get an API Key. The API key belongs to a user, so it is available under Account -> NuGet API Key -> Access API Key.

An important thing is that the key won’t work if you don’t activate the NuGet API Key Security Realm. This is mentioned in the documentation but it’s not intuitive (i.e. why do you get a NuGet repository out of the box, but it’s effectively read-only?). To enable it, simply go to Security -> Realms and add the NuGet API Key Security Realm to the list of Active realms.

Reading packages from the NuGet repository does not use the API Key but your regular credentials. This might be great for personal use, but it becomes tricky in CI, especially if we are restoring packages inside a Docker container.

The solution I ended up using is to create a NuGet.Config file in the project where I want to use my private NuGet packages. NuGet will take this file into account. In that file, I specify my private feed source:

<?xml version="1.0" encoding="utf-8"?>
<configuration>
  <packageSources>
    <add key="nuget.org" value="https://api.nuget.org/v3/index.json" protocolVersion="3" />
    <add key="nexus" value="http://my-server:8081/repository/nuget-hosted/" />
  </packageSources>
  <packageRestore>
    <add key="enabled" value="True" />
    <add key="automatic" value="True" />
  </packageRestore>
  <bindingRedirects>
    <add key="skip" value="False" />
  </bindingRedirects>
  <packageManagement>
    <add key="format" value="0" />
    <add key="disabled" value="False" />
  </packageManagement>
  <disabledPackageSources />
  <packageSourceCredentials>
    <nexus>
      <add key="Username" value="viewer" />
      <add key="ClearTextPassword" value="unfortunately it is not encrypted" />
    </nexus>
  </packageSourceCredentials>
</configuration>

Unfortunately, the password needs to be unencrypted. NuGet supports encryption, but only for Windows. If you try to use the encrypted password on a Linux build agent (or inside Docker), you get a helpful error message telling you that encryption is at the moment only supported in Windows. When life gives you lemons, you improvise I guess. So my solution here is to create a dedicated user in Nexus (named ‘viewer’ in this example) that only has the minimum rights needed to read from the NuGet feed. The advantage is that everyone who has access to the source code can instantly access the private NuGet feed without any extra configuration.