Find any version of any Nix package, instantly.
nxv indexes the entire nixpkgs git history to help you discover when packages were added, which versions existed, and the exact commit to use with nix shell nixpkgs/<commit>#package.
Because sometimes you need Python 2.7 for that legacy project nobody wants to touch. Or Ruby 2.6 because the Gemfile hasn't been updated since the Obama administration. Instead of spending your afternoon spelunking through GitHub commits and praying to the Nix gods, just ask nxv. It's indexed 8+ years of nixpkgs history so you don't have to.
No installation required — query the live API directly:
# Search for Node.js 15.x
NXV_API_URL=https://nxv.urandom.io nix run github:jamesbrink/nxv -- search nodejs 15
# Find Python 2.7
NXV_API_URL=https://nxv.urandom.io nix run github:jamesbrink/nxv -- search python 2.7Or visit https://nxv.urandom.io to search in your browser.
- Fast search — Bloom filter for instant "not found" responses, SQLite FTS5 for full-text search
- Version history — See when each version was introduced and when it was superseded
- Multiple interfaces — CLI tool, HTTP API server with web UI, or query via remote API
- NixOS module — Run as a systemd service with automatic index updates
- Lightweight — ~7MB static binary, ~100MB compressed index
┌─────────────────┐ ┌─────────────────┐ ┌─────────────────┐
│ nixpkgs │────▶│ Indexer │────▶│ SQLite Index │
│ git history │ │ (nix eval per │ │ + Bloom Filter │
│ │ │ commit) │ │ │
└─────────────────┘ └─────────────────┘ └─────────────────┘
│
▼
┌─────────────────┐ ┌─────────────────┐ ┌─────────────────┐
│ nxv search │◀────│ Query Engine │◀────│ Download from │
│ nxv serve │ │ (bloom + FTS5) │ │ remote/local │
└─────────────────┘ └─────────────────┘ └─────────────────┘
The indexer walks nixpkgs commits (from 2017+), runs nix eval to extract package metadata, and stores version ranges in SQLite.
Users download a pre-built compressed index (~100MB) and query it locally or via the API server.
curl -sSfL https://raw.githubusercontent.com/jamesbrink/nxv/main/install.sh | shThis installs a pre-built binary to ~/.local/bin. For extra safety, download and review the script first. Set NXV_VERIFY=1 to enforce checksum verification against GitHub Releases.
cargo install nxv# Run directly
nix run github:jamesbrink/nxv -- search python
# Install to profile
nix profile install github:jamesbrink/nxvOr add to your flake:
{
inputs.nxv.url = "github:jamesbrink/nxv";
outputs = { nixpkgs, nxv, ... }: {
nixosConfigurations.myhost = nixpkgs.lib.nixosSystem {
modules = [{
nixpkgs.overlays = [ nxv.overlays.default ];
environment.systemPackages = [ pkgs.nxv ];
}];
};
};
}Download from GitHub Releases:
| Platform | Binary |
|---|---|
| Linux x86_64 | nxv-x86_64-linux-musl (static) |
| Linux aarch64 | nxv-aarch64-linux-musl (static) |
| macOS x86_64 | nxv-x86_64-apple-darwin |
| macOS Apple Silicon | nxv-aarch64-apple-darwin |
Shell completions for bash, zsh, and fish are included via nxv completions <shell>.
nxv search python # Find all python packages
nxv search python 3.11 # Filter by version prefix
nxv search python --exact # Exact name match only
nxv search "json parser" --desc # Search descriptions (FTS)
nxv search python --format json # JSON outputnxv info python # Detailed package information
nxv info python 3.11.0 # Info for specific version
nxv history python # Version timeline
nxv history python 3.11.0 # When was 3.11.0 available?# After finding a commit hash from search results:
nix shell nixpkgs/e4a45f9#python
nix run nixpkgs/e4a45f9#pythonnxv update # Download/update the index
nxv update --force # Force full re-download
nxv stats # Show index statisticsRun nxv as an HTTP API server with a built-in web interface:
nxv serve # localhost:8080
nxv serve --host 0.0.0.0 --port 3000 --cors # Public with CORSEndpoints:
| Endpoint | Description |
|---|---|
GET / |
Web UI |
GET /docs |
OpenAPI documentation (Scalar) |
GET /api/v1/search?q=python |
Search packages |
GET /api/v1/search/description?q=json |
Search descriptions |
GET /api/v1/packages/{attr} |
Package details |
GET /api/v1/packages/{attr}/history |
Version history |
GET /api/v1/packages/{attr}/versions/{version} |
Specific version info |
GET /api/v1/packages/{attr}/versions/{version}/first |
First occurrence commit |
GET /api/v1/packages/{attr}/versions/{version}/last |
Last occurrence commit |
GET /api/v1/stats |
Index statistics |
GET /api/v1/health |
Health check |
Point the CLI at a remote server instead of using a local database:
export NXV_API_URL=http://your-server:8080
nxv search python # Uses remote API transparentlyRun the API server as a systemd service with automatic updates:
{
inputs.nxv.url = "github:jamesbrink/nxv";
outputs = { nixpkgs, nxv, ... }: {
nixosConfigurations.myhost = nixpkgs.lib.nixosSystem {
modules = [
nxv.nixosModules.default
{
services.nxv = {
enable = true;
host = "0.0.0.0";
port = 8080;
dataDir = "/var/lib/nxv";
cors.enable = true;
openFirewall = true;
autoUpdate.enable = true; # Daily index updates
};
}
];
};
};
}All module options
| Option | Default | Description |
|---|---|---|
enable |
false |
Enable the nxv API service |
host |
127.0.0.1 |
Address to bind to |
port |
8080 |
Port to listen on |
dataDir |
/var/lib/nxv |
Directory for index.db |
manifestUrl |
null |
Custom manifest URL for self-hosted index |
publicKey |
null |
Custom public key for signature verification (path or raw key) |
skipVerify |
false |
Skip signature verification (insecure) |
cors.enable |
false |
Enable CORS for all origins |
cors.origins |
null |
Specific allowed origins |
openFirewall |
false |
Open firewall port |
autoUpdate.enable |
false |
Enable automatic index updates |
autoUpdate.interval |
daily |
Update frequency (systemd calendar syntax) |
Run nxv as a container using the official image from GitHub Container Registry:
# Run the API server (default command)
docker run -p 8080:8080 ghcr.io/jamesbrink/nxv:latest
# With persistent index storage
docker run -p 8080:8080 -v nxv-data:/root/.local/share/nxv ghcr.io/jamesbrink/nxv:latest
# Run other commands
docker run ghcr.io/jamesbrink/nxv:latest search python
docker run ghcr.io/jamesbrink/nxv:latest --help
# Build an index (requires nixpkgs mount)
docker run -v ./nixpkgs:/nixpkgs -v nxv-data:/root/.local/share/nxv \
ghcr.io/jamesbrink/nxv:latest index --nixpkgs-path /nixpkgsTags:
latest— Latest build from main branchx.y.z— Specific version (e.g.,0.1.0)
The image includes the indexer feature, git, and CA certificates. By default it runs nxv serve on port 8080.
# Build the Docker image (Linux only)
nix build .#packages.x86_64-linux.nxv-docker
# Load into Docker
docker load < resultThe self-hosting workflow is:
- Build — Run
nxv indexagainst a nixpkgs clone to create the SQLite database - Publish — Run
nxv publishto generate compressed artifacts with a manifest - Host — Upload artifacts to any static file host (S3, GitHub Releases, etc.)
- Configure — Point clients at your manifest via
NXV_MANIFEST_URL
Requires the indexer feature and a local nixpkgs clone:
# Build with indexer support
nix build .#nxv-indexer
# or: cargo build --release --features indexer
# Clone nixpkgs (full history needed for complete index)
git clone https://github.com/NixOS/nixpkgs.git
# Build the index (takes hours for full history)
nxv index --nixpkgs-path ./nixpkgs --full
# Incremental update (much faster)
nxv index --nixpkgs-path ./nixpkgsUpdate missing fields without full rebuild:
# Fast: extract from current nixpkgs HEAD
nxv backfill --nixpkgs-path ./nixpkgs
# Accurate: traverse git history to original commits
nxv backfill --nixpkgs-path ./nixpkgs --historyGenerate distribution-ready artifacts with the publish command:
# Generate compressed index, bloom filter, and manifest
nxv publish --output ./publish --url-prefix https://your-server.com/nxv
# Files created:
# publish/index.db.zst - Compressed SQLite database (~100MB)
# publish/bloom.bin - Bloom filter for fast lookups (~26KB)
# publish/manifest.json - Manifest with URLs and checksumsThe --url-prefix sets the base URL that will appear in the manifest. This should match where you'll host the files.
manifest.jsonincludes SHA256 checksums for all artifacts- Manifests are cryptographically signed with minisign and verified on download
- To roll back a bad index, re-upload a previous
index.db.zst,bloom.bin, andmanifest.jsonto the same location or pointNXV_MANIFEST_URLat a known-good manifest
For self-hosted indexes, sign your manifest to enable signature verification.
# Generate a keypair (one-time, requires indexer feature)
nxv keygen --secret-key nxv.key --public-key nxv.pub
# Publish with signing in one step
nxv publish --output ./publish --url-prefix https://your-server/nxv --sign --secret-key ./nxv.key
# Or use NXV_SECRET_KEY environment variable (useful for CI/CD)
export NXV_SECRET_KEY=/path/to/nxv.key # Can also be raw key content
nxv publish --output ./publish --url-prefix https://your-server/nxv --sign
# Files created:
# publish/index.db.zst - Compressed database
# publish/bloom.bin - Bloom filter
# publish/manifest.json - Manifest with URLs and checksums
# publish/manifest.json.minisig - Signature fileClients can then verify using your public key:
# Via CLI flag
nxv update --manifest-url https://your-server/manifest.json \
--public-key /path/to/nxv.pub
# Or via environment variable
export NXV_PUBLIC_KEY=/path/to/nxv.pub
nxv update --manifest-url https://your-server/manifest.jsonTo skip verification (not recommended for production):
nxv update --manifest-url https://your-server/manifest.json --skip-verifyYou can host the published artifacts anywhere that serves static files:
GitHub Releases
# Generate artifacts
nxv publish --output ./publish \
--url-prefix https://github.com/YOUR_USER/YOUR_REPO/releases/download/index-latest
# Create release and upload
gh release create index-latest \
--title "Package Index" \
--notes "nxv package index" \
--latest=false
gh release upload index-latest publish/* --clobberAmazon S3
# Generate artifacts
nxv publish --output ./publish \
--url-prefix https://your-bucket.s3.amazonaws.com/nxv
# Upload to S3
aws s3 sync ./publish s3://your-bucket/nxv/ --acl public-readCloudflare R2
# Generate artifacts
nxv publish --output ./publish \
--url-prefix https://your-bucket.r2.cloudflarestorage.com/nxv
# Upload using rclone or wrangler
rclone sync ./publish r2:your-bucket/nxv/Any static file server
# Generate artifacts
nxv publish --output ./publish \
--url-prefix https://your-server.com/nxv
# Copy to your web server
rsync -av ./publish/ your-server:/var/www/nxv/There are two ways clients can consume your index:
Clients download the index once and query locally. Low server load, works offline after initial download.
# One-time download
nxv update --manifest-url https://your-server.com/nxv/manifest.json
# Or set permanently
export NXV_MANIFEST_URL=https://your-server.com/nxv/manifest.jsonRun nxv serve to provide a web UI and REST API. Clients query remotely without downloading the index. Good for shared/team environments or the web UI.
# Server side
nxv serve --host 0.0.0.0 --port 8080
# Client side
export NXV_API_URL=http://your-server:8080
nxv search python # Queries remote APIRuns the API server with auto-updates from your manifest:
services.nxv = {
enable = true;
manifestUrl = "https://your-server.com/nxv/manifest.json";
host = "0.0.0.0";
autoUpdate.enable = true;
};Manifest format reference
The manifest.json format:
{
"version": 1,
"latest_commit": "abc123def456789...",
"latest_commit_date": "2024-01-15T12:00:00Z",
"full_index": {
"url": "https://your-server.com/nxv/index.db.zst",
"size_bytes": 150000000,
"sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855"
},
"bloom_filter": {
"url": "https://your-server.com/nxv/bloom.bin",
"size_bytes": 150000,
"sha256": "d7a8fbb307d7809469ca9abcb0082e4f8d5651e46d3cdb762d02d0bf37c9e592"
},
"deltas": []
}| Variable | Description |
|---|---|
NXV_DB_PATH |
Path to index database (bloom filter stored as sibling file) |
NXV_API_URL |
Remote API URL (CLI uses remote instead of local DB when set) |
NXV_MANIFEST_URL |
Custom manifest URL for index downloads |
NXV_PUBLIC_KEY |
Custom public key for manifest verification (path or raw key) |
NXV_SECRET_KEY |
Secret key for manifest signing (path or raw key content) |
NXV_SKIP_VERIFY |
Skip manifest signature verification (set to any value) |
NXV_API_TIMEOUT |
API request timeout in seconds (default: 30) |
NO_COLOR |
Disable colored output |
NXV_INSTALL_DIR |
Custom install directory for curl installer (default: ~/.local/bin) |
NXV_VERSION |
Specific version for curl installer (default: latest) |
nix develop # Enter dev shell
cargo build # Debug build
cargo build --features indexer # With indexer
cargo test # Run tests
cargo test --features indexer # All tests including indexer
cargo clippy -- -D warnings # Lintsrc/
├── main.rs # Entry point, command dispatch
├── cli.rs # Clap command definitions
├── backend.rs # Backend abstraction (local/remote)
├── client.rs # HTTP client for remote API
├── completions.rs # Shell completion generation
├── db/ # SQLite database layer
├── remote/ # Index download/update
├── server/ # HTTP API (axum)
├── output/ # Table/JSON/plain formatters
├── bloom.rs # Bloom filter
├── search.rs # Search/filter/sort logic
├── paths.rs # Platform-specific paths
├── error.rs # Error types
└── index/ # Indexer (feature-gated)
├── mod.rs # Indexer orchestration
├── git.rs # Git history traversal
├── extractor.rs # Nix evaluation
├── backfill.rs # Metadata backfill
└── publisher.rs # Index publishing
There are several other great tools in this space:
| Tool | Approach | Link |
|---|---|---|
| nix-community/nix-index | Indexes files in nixpkgs for nix-locate — find which package has a file |
github.com/nix-community/nix-index |
| lazamar/nix-package-versions | Web service that samples nixpkgs every ~5 weeks | lazamar.co.uk/nix-versions |
| NixHub.io | Parses Hydra build outputs, provides web UI and API | nixhub.io |
| vic/nix-versions | CLI that aggregates lazamar, nixhub, and history APIs | github.com/vic/nix-versions |
| history.nix-packages.com | Web-based package history browser | history.nix-packages.com |
nxv takes a different approach: it indexes every commit locally, giving you a complete offline-capable database with no gaps. Choose what works best for your use case!
