A nuget server created with linux-first approach.
There seems to be no good nuget server for hosting private nuget packages and caching, when working mainly with linux and dotnet core. Running windows just to host a several nuget packages seems like a big waste.
This project aims at following:
- provide self-hosted nuget server for private package hosting.
- hosted with kestrel on dotnet core 2.0
- released as ready to use docker image, preferably to be deployed to kubernetes.
- continuously tested with paket including several common project setup cases
- good performance when server is used by multiple clients, such as CI agents building various projects, downloading lots of packages at the same time.
- easy to develop on linux in VS Code, not only in VS on windows.
- if possible, implement caching mode for public packages from nuget.org
- Limited NuGet V2 API for hosting private packages. Includes endpoints
FindPackagesById()
,Packages()
andPUT /api/v2
. Which is sufficient for clients to download, push, find or restore packages. - Caching proxy of with limited NuGet V3 API. It intercepts responses from selected
services of
https://api.nuget.org/v3/index.json
replacinghttps://api.nuget.org/v3
by local LiGet server URL.- Allows to cache
.nupkg
packages on server, rather than downloading them from the Internet each time. - Caches package metadata and invalidates when upstream changes are detected using NuGet.CatalogReader.
- For end user effect is similar to running a mirror of nuget.org, but instead of downloading all packages, cache keeps only the ones which were ever requested.
- Allows to cache
Not implemented:
- V2 search, filter and alike queries. These seem to used only by UI or nuget gallery.
- Authentication and user-based access. Currently the server is open for all requests.
For dotnet CLI and nuget you need to configure nuget config ~/.nuget/NuGet/NuGet.Config
with something like:
<?xml version="1.0" encoding="utf-8"?>
<configuration>
<packageSources>
<add key="nuget.org" value="https://www.nuget.org/api/v2" protocolVersion="2" />
<add key="liget" value="http://liget:9011/api/v2" protocolVersion="2" />
</packageSources>
</configuration>
For paket, in paket.dependencies
, just specify another source:
source http://liget:9011/api/v2
For dotnet CLI and nuget you need to configure nuget config ~/.nuget/NuGet/NuGet.Config
with something like:
<?xml version="1.0" encoding="utf-8"?>
<configuration>
<packageSources>
<add key="liget-proxy" value="http://liget:9011/api/cache/v3/index.json" protocolVersion="3" />
<add key="liget" value="http://liget:9011/api/v2" protocolVersion="2" />
</packageSources>
</configuration>
For paket, in paket.dependencies
, just specify liget as the 2 only sources
source http://liget:9011/api/cache/v3/index.json
# public packages...
source http://liget:9011/api/v2
# private packages...
The simplest start command is
mkdir -p /tmp/liget-test
docker run -p 9011:9011 -v /tmp/liget-test/:/data tomzo/liget
- Default current directory
/data
. - Main process starts with tini as root,
then drops privileges to run as
liget
user withdotnet
.
For best reference see the docker directory with Dockerfile and startup script.
All packages, cache, and temporary data is stored in /data
.
By default in /data/<backend>
.
/data
will be always owned by liget
. Startup script switches uid/gid at start
to fit with whatever was mounted from the host.
The exception to this is when /data
is owned by root
, then liget has to run as root
.``
Everything can be configured with environment variables:
LIGET_BACKEND
by defaultsimple
. Currently the only implementationLIGET_SIMPLE_ROOT_PATH
- root directory used bysimple
backend. By default/data/simple
.LIGET_BACKGROUND_TASKS
- run background tasks periodically. By defaulttrue
.LIGET_FS_MONITORING
- monitorLIGET_SIMPLE_ROOT_PATH
for changes. By defaulttrue
, which allows to drop packages directly toLIGET_SIMPLE_ROOT_PATH
to be added to repo.LIGET_ALLOW_OVERWRITE
, by defaultfalse
. Whentrue
allows push to replace previous package with same version.LIGET_FRAMEWORK_FILTERING
, by defaulttrue
. Not implemented.LIGET_ENABLE_DELISTING
, by defaulttrue
. Not implemented.LIGET_IGNORE_SYMBOLS
, by defaultfalse
. Not implemented.
Every dotnet Core application has .runtimeconfig.json
, which can configure garbage collector.
You may want to set following:
LIGET_GC_CONCURRENT
- by defaulttrue
LIGET_GC_SERVER
- by defaulttrue
, beware though that this may cause higher memory use.LIGET_THREAD_POOL_MIN
- minimal number of worker threads. By default 16.LIGET_THREAD_POOL_MAX
- minimal number of worker threads. By default 32.
Kestrel specific:
LIGET_LIBUV_THREAD_COUNT
- number of libuv threads handling the requests. By default not set, determined by libuv default.
LIGET_CACHE_PROXY_SOURCE_INDEX
- address of original V3 API to cache. By defaulthttps://api.nuget.org/v3/index.json
.LIGET_CACHE_INVALIDATION_CHECK_PERIOD
- defines frequency at which a check with upstream server is made to see if cache is invalid. By default60
(seconds).LIGET_NUPKG_CACHE_BACKEND
- backend of the .nupkg caching proxy. By defaultdbreeze
, which, currently is the only implementation.LIGET_NUPKG_CACHE_DBREEZE_ROOT_PATH
- root directory where dbreeze will store cached packages. By default/data/cache/dbreeze
.LIGET_NUPKG_CACHE_DBREEZE_BACKEND
- storage backend of dbreeze, can bedisk
ormemory
. By defaultdisk
.
LIGET_LOG_LEVEL
- by defaultINFO
.LIGET_LOG_BACKEND
- by defaultconsole
. Also can begelf
orcustom
.
Default logging is to console. log4net
is configured by /etc/liget/log4net.xml
.
If you set LIGET_LOG_BACKEND=custom
then it is expected that you will provide /etc/liget/log4net.xml
.
Logging to graylog.
LIGET_LOG_GELF_HOST
- no default. But should be configured whenLIGET_LOG_BACKEND=gelf
LIGET_LOG_GELF_PORT
- by default12201
.
We are running load tests against the server under following setup:
- There is a dedicated 2-core VM with capped IO limits on disk:
- max read bytes/sec 64 MB/s
- max write bytes/sec 32 MB/s
- max read IO ops/s 3000
- max write IO ops/s 1200
- On the VM we start 2 docker containers with docker-compose, server has limit on memory of 550MB.
- LiGet is configured with defaults and:
LIGET_LIBUV_THREAD_COUNT=16
The stress tests run
- paket install to download about 500MB of packages, via liget caching proxy V3 API.
- nuget push of each package to repository using V2 API.
- Then 6 workers run
paket install -f
via liget caching proxy V3 API.
Under these conditions:
- cache download performance is good. The 6 workers end download within 9 minutes and are mostly constrained by IO limit. Same test on my local machine on SSD drive, passes in 3 minutes.
- memory usage reported by docker stats peaks at about 520 MB. So docker mem_limit of 550MB-600MB makes sense.
All building and tests are done with IDE and docker.
dotnet restore
dotnet build
Or
./tasks.sh build
./tasks.sh build
./tasks.sh test
Firsly, this project is using lots of code from other nuget servers, either as reference or actually porting pieces of code. Credits:
- TanukiSharp/MinimalNugetServer as minimal dotnet core setup
- emresenturk/NetCoreNugetServer another minimal dotnet core server
- official NuGet.Server
- NuGet.Lucene which is part of klondike
This project is licenced under Apache License 2.0.