Skip to content

kekyo/nuget-server

Repository files navigation

nuget-server

Simple modenized NuGet server implementation on Node.js

nuget-server

Project Status: WIP – Initial development is in progress, but there has not yet been a stable, usable release suitable for the public. License: MIT npm version


What is this?

A simple NuGet server implementation built on Node.js that provides essential NuGet v3 API endpoints.

Compatible with dotnet restore and standard NuGet clients for package publishing, querying, and downloading.

A modern browser-based UI is also provided:

  • You can refer to registered packages. You can check various package attributes.
  • You can download packages by version.
  • You can also publish (upload) packages.
  • You can manage user accounts.

Browse package list:

Browse package list

Publishing packages:

Publishing packages

User account managements:

User account managements

Key Features

  • Easy setup, run NuGet server in 10 seconds!
  • NuGet V3 API compatibility: Support for modern NuGet client operations
  • No need database management: Store package file and nuspecs into filesystem directly, feel free any database managements
  • Package publish: Flexible client to upload .nupkg files via HTTP POST using cURL and others
  • Basic authentication: Setup authentication for publish and general access when you want it
  • Reverse proxy support: Configurable trusted reverse proxy handling for proper URL resolution
  • Modern Web UI with enhanced features:
    • Multiple package upload: Drag & drop multiple .nupkg files at once
    • User account management: Add/delete users, reset passwords (admin only)
    • API password regeneration: Self-service API password updates
    • Password change: Users can change their own passwords
  • Docker image available

Installation

npm install -g nuget-server

Usage

# Start server on default port 5963
nuget-server

# Custom port
nuget-server --port 3000

# Custom log level (debug, info, warn, error, ignore - default: info)
nuget-server --log-level debug

The NuGet V3 API is served on the /v3 path.

  • Default nuget-server served URL (Show UI): http://localhost:5963
  • Actual NuGet V3 API endpoint: http://localhost:5963/v3/index.json

Default nuget-server served URL can change with --base-url option, it shows below section.

Configure the NuGet client

Add nuget-server as package source

Add as package source:

dotnet nuget add source http://localhost:5963/v3/index.json \
  -n "local" --allow-insecure-connections

Or specify in nuget.config:

<?xml version="1.0" encoding="utf-8"?>
<configuration>
  <packageSources>
    <add key="local" value="http://localhost:5963/v3/index.json"
      allowInsecureConnections="true" />
  </packageSources>
</configuration>

Publish packages

Upload packages by HTTP POST method, using cURL or any HTTP client with /api/publish endpoint:

# Upload "MyPackage.1.0.0.nupkg" file
curl -X POST http://localhost:5963/api/publish \
  --data-binary @MyPackage.1.0.0.nupkg \
  -H "Content-Type: application/octet-stream"

This methodology uses simply HTTP POST with binary (octet-stream) instead of the standard NuGet V3 publish protocol (dotnet nuget push command), because it is gateway,reverse-proxy,load-balancer friendy access.

Package storage configuration

Storage location

By default, packages are stored in the ./packages directory relative to where you run nuget-server. You can customize this location using the --package-dir option:

# Use default ./packages directory
nuget-server

# Use custom directory (relative or absolute path)
nuget-server --package-dir /another/package/location

Package storage layout

Packages are stored in the filesystem using the following structure:

packages/
β”œβ”€β”€ PackageName/
β”‚   β”œβ”€β”€ 1.0.0/
β”‚   β”‚   β”œβ”€β”€ PackageName.1.0.0.nupkg
β”‚   β”‚   β”œβ”€β”€ PackageName.nuspec
β”‚   β”‚   └── icon.png            # Package icon (if present)
β”‚   └── 2.0.0/
β”‚       β”œβ”€β”€ PackageName.2.0.0.nupkg
β”‚       β”œβ”€β”€ PackageName.nuspec
β”‚       └── icon.jpg            # Package icon (if present)
└── AnotherPackage/
    └── 1.5.0/
        β”œβ”€β”€ AnotherPackage.1.5.0.nupkg
        β”œβ”€β”€ AnotherPackage.nuspec
        └── icon.png            # Package icon (if present)

Backup and restore

You can backup the package directory using simply tar or other achiver:

cd /your/server/base/dir
tar -cf - ./packages | lz4 > backup-packages.tar.lz4

Restore is simply extract it and re-run nuget-server with the same package directory configuration.


Configuration directory

You can specify a custom configuration directory:

# Using command line option
nuget-server --config-dir /path/to/config

# Using environment variable
export NUGET_SERVER_CONFIG_DIR=/path/to/config
nuget-server

The configuration directory is used for the following configuration file (config.json) and basic authentication file (users.json).

Configuration file (config.json)

nuget-server supports configuration through a config.json file located in the configuration directory. This provides an alternative to command-line options and environment variables.

Configuration priority

Settings are applied in the following order (highest to lowest priority):

  1. Command-line options
  2. Environment variables
  3. config.json
  4. Default values

config.json structure

Create a config.json file in your configuration directory:

{
  "port": 5963,
  "baseUrl": "http://localhost:5963",
  "packageDir": "./packages",
  "realm": "Awsome nuget-server",
  "logLevel": "info",
  "noUi": false,
  "trustedProxies": ["127.0.0.1", "::1"],
  "authMode": "none",
  "sessionSecret": "<your-secret-here>",
  "passwordMinScore": 2,
  "passwordStrengthCheck": true
}

All fields are optional. Only include the settings you want to override.

JSON-based authentication with --auth-init

In addition to htpasswd authentication, nuget-server also supports JSON-based authentication with role management.

Initialize with --auth-init

Create an initial admin user interactively:

nuget-server --auth-init --config-dir ./config

This command will:

  1. Prompt for admin username (default: admin)
  2. Prompt for password (with strength checking, masked input)
  3. Generate an API password for the admin user
  4. Create users.json in the config directory
  5. Exit after initialization (server does not start)

Non-interactive mode (CI/CD)

For automated deployments, you can provide credentials via environment variables:

export NUGET_SERVER_ADMIN_USERNAME=admin
export NUGET_SERVER_ADMIN_PASSWORD=MySecurePassword123!
nuget-server --auth-init --config-dir ./config

This allows initialization in CI/CD pipelines without user interaction.

Example session:

Initializing authentication...
Enter admin username [admin]:
Enter password: ****
Confirm password: ****

============================================================
Admin user created successfully!
============================================================
Username: admin
Password: *********************
API password: ngs_xxxxxxxxxxxxxxxxxxxxxx
============================================================

IMPORTANT: Save this API password securely. It cannot be retrieved again.
Use this API password for NuGet client authentication:
Example register: dotnet nuget add source "http://localhost:5963/v3/index.json"
  -n ref1 -u admin -p ngs_xxxxxxxxxxxxxxxxxxxxxx
  --store-password-in-clear-text --allow-insecure-connections
============================================================

Authentication modes

When using JSON-based authentication, configure the mode with --auth-mode:

  • none: No authentication required (default)
  • publish: Authentication required only for package publishing
  • full: Authentication required for all operations

Using the API password

After initialization, use the generated API password with NuGet clients:

# Add source with API password
dotnet nuget add source http://localhost:5963/v3/index.json \
  -n "local" \
  -u admin \
  -p ngs_xxxxxxxxxxxxxxxxxxxxxx \
  --store-password-in-clear-text --allow-insecure-connections

Or specify nuget.config with credentials:

<?xml version="1.0" encoding="utf-8"?>
<configuration>
  <packageSources>
    <add key="local" value="http://localhost:5963/v3/index.json"
      allowInsecureConnections="true" />
  </packageSources>
  <packageSourceCredentials>
    <local>
      <add key="Username" value="reader" />
      <add key="ClearTextPassword" value="your-password" />
    </local>
  </packageSourceCredentials>
</configuration>

For package publishing:

# Publish packages with API password
curl -X POST http://localhost:5963/api/publish \
  -u admin:ngs_xxxxxxxxxxxxxxxxxxxxxx \
  --data-binary @MyPackage.1.0.0.nupkg \
  -H "Content-Type: application/octet-stream"

Note: The users.json file should be protected with appropriate file permissions and never committed to version control.

Password strength requirements

nuget-server uses the zxcvbn library to enforce strong password requirements:

  • Evaluates password strength on a scale of 0-4 (Weak to Very Strong)
  • Default minimum score: 2 (Good)
  • Checks against common passwords, dictionary words, and patterns
  • Provides real-time feedback during password creation

Configure password requirements in config.json:

{
  "passwordMinScore": 2, // 0-4, default: 2 (Good)
  "passwordStrengthCheck": true // default: true
}

Reverse proxy interoperability

The server supports running behind reverse proxies with proper URL resolution.

The server resolves URLs using the following priority order:

  1. Fixed base URL (highest priority): When --base-url option is specified, it always takes precedence
  2. Trusted proxy headers: When trusted proxies are configured with --trusted-proxies:
    • RFC 7239 compliant Forwarded header (proto, host, port)
    • Traditional X-Forwarded-* headers (X-Forwarded-Proto, X-Forwarded-Host, X-Forwarded-Port)
  3. Standard request information (fallback): Uses Host header when proxy headers are not available

For example --base-url option:

  • nuget-server served public base URL: https://packages.example.com
  • Actual NuGet V3 API endpoint: https://packages.example.com/v3/index.json
# Configure served URL (do not include /api path)
nuget-server --base-url https://packages.example.com

Another option, you can configure with trusted proxy addresses:

# Configure trusted proxies for proper host header handling
nuget-server --trusted-proxies "10.0.0.1,192.168.1.100"

Environment variables are also supported:

export NUGET_SERVER_BASE_URL=https://packages.example.com
export NUGET_SERVER_TRUSTED_PROXIES=10.0.0.1,192.168.1.100
export NUGET_SERVER_CONFIG_DIR=/path/to/config
export NUGET_SERVER_SESSION_SECRET=your-secret-key-here

Security Configuration

Session Security

For production deployments, always set a secure session secret:

export NUGET_SERVER_SESSION_SECRET=$(openssl rand -base64 32)
nuget-server

(Or use config.json.)

If not set, a random secret is generated (warning will be logged). The session secret is crucial for securing web UI sessions.

Supported NuGet V3 API endpoints

The server implements a subset of the NuGet V3 API protocol:

  • Service index: /v3/index.json
  • Package content: /v3/package/{id}/index.json
  • Package downloads: /v3/package/{id}/{version}/{filename}
  • Registration index: /v3/registrations/{id}/index.json

Docker Usage (WIP)

Building the Docker Image

Use the provided build script (require podman):

./build-docker.sh

Or build manually:

# Build the image
docker build -t nuget-server:latest .

# Tag for Docker Hub (replace with your username)
docker tag nuget-server:latest yourusername/nuget-server:latest

Running with Docker (WIP)

TODO: Docker setup is constructing.

Basic usage

# Run with default settings (port 5963, packages stored in mounted volume)
docker run -p 5963:5963 -v $(pwd)/packages:/packages nuget-server:latest

# With authentication configuration directory
docker run -p 5963:5963 \
  -v $(pwd)/packages:/packages \
  -v $(pwd)/config:/config \
  nuget-server:latest

Custom configuration

# Custom port (using Docker port forwarding)
docker run -p 3000:5963 -v $(pwd)/packages:/packages nuget-server:latest

# With base URL for reverse proxy
docker run -p 5963:5963 -v $(pwd)/packages:/packages \
  nuget-server:latest --base-url https://nuget.example.com

# Multiple options
docker run -p 3000:5963 -v $(pwd)/packages:/packages \
  nuget-server:latest \
  --base-url https://nuget.example.com \
  --trusted-proxies "10.0.0.1,192.168.1.100"

Using environment variables

docker run -p 5963:5963 \
  -v $(pwd)/packages:/packages \
  -e NUGET_SERVER_BASE_URL=https://nuget.example.com \
  -e NUGET_SERVER_TRUSTED_PROXIES=10.0.0.1 \
  nuget-server:latest

Volume mounts

  • /packages: Package storage directory (should be mounted to persist data)
  • /config: Configuration directory for htpasswd files (optional)

The Docker image uses fixed directories internally, but you can mount any host directories to these locations.


License

Under MIT.

About

Simple modenized NuGet server πŸ“¦

Topics

Resources

License

Stars

Watchers

Forks