Simple modenized NuGet server implementation on Node.js
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:
Publishing packages:
User account managements:
- 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 viaHTTP 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
npm install -g nuget-server
# 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.
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>
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.
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
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)
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.
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
).
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.
Settings are applied in the following order (highest to lowest priority):
- Command-line options
- Environment variables
- config.json
- Default values
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.
In addition to htpasswd authentication, nuget-server also supports JSON-based authentication with role management.
Create an initial admin user interactively:
nuget-server --auth-init --config-dir ./config
This command will:
- Prompt for admin username (default: admin)
- Prompt for password (with strength checking, masked input)
- Generate an API password for the admin user
- Create
users.json
in the config directory - Exit after initialization (server does not start)
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
============================================================
When using JSON-based authentication, configure the mode with --auth-mode
:
none
: No authentication required (default)publish
: Authentication required only for package publishingfull
: Authentication required for all operations
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.
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
}
The server supports running behind reverse proxies with proper URL resolution.
The server resolves URLs using the following priority order:
- Fixed base URL (highest priority): When
--base-url
option is specified, it always takes precedence - 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
)
- RFC 7239 compliant
- 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
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.
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
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
TODO: Docker setup is constructing.
# 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 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"
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
/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.
Under MIT.