Skip to main content

Nazgarr

Nazgarr

Your media library, your seeding folders and your trackers, in one place.
Latest release License: GPL-3.0

Nazgarr is a self-hosted web app for anyone who seeds on private trackers from the same files their media library uses. It reads your disks and finds the hardlinks between library and seeding folders. It also knows what your torrent clients are really seeding, finds what could seed again, and publishes your own uploads to your trackers.

It isn't tied to Unraid or to the *arr stack. Separate disks without FUSE or RAID work, and Radarr, Sonarr and the rest are optional integrations, never dependencies. The name only borrows the *arr naming style, like Bazarr or Prowlarr.

Nothing that touches your files or your torrent clients runs without your approval. Every hardlink and every torrent added to a client goes through a review queue first. Running them automatically is an opt-in setting, off by default.

What it does

See what seeds and what doesn't

  • Every file of the library and of the seeding folders gets a state: seeding (a hardlink is seeding in a client), orphaned, or tracked by a client without a link in the library.
  • Folder and poster views, filters by state, size and tracker, duplicates, and an "exclude" for the files that never count.
  • A dashboard with the library health (how much of it seeds, by size), its trend over time, and file-by-file changes since the last scan.

Reseed what you already have

  • Library files that seed nowhere are searched on your trackers by TMDB id. Candidates are confirmed by reading the torrent's piece hashes against your files, not just by names and sizes.
  • Optionally, cross-seed: a file seeding on one tracker is also searched on the others.
  • Matches wait in a review queue with their confidence and what they would do. After your approval, Nazgarr creates the hardlinks and adds the torrent with a real recheck.

Triage your seeding folder

  • Torrents seeding without a hardlink in the library are listed with their reason: replaced by an upgrade, a copy, removed from the library, never imported, extras only.
  • Each tracker can set its minimum seed time and ratio. Triage then marks with a green OK the torrents you can remove safely. Anything else to check (files shared with another torrent, a client error) sits in a popover next to it.

Upload your releases

  • A guided flow: TMDB identification with a confidence explained factor by factor, then a dupe check on every tracker. When the tracker already has the same release, a full hash check proves it and you reseed instead of uploading.
  • Release names per tracker from editable patterns: resolution, source, HDR and Dolby Vision profile, audio, languages, REMUX and HYBRID detection.
  • MediaInfo, screenshots uploaded to an image host (with fallback across hosts), and descriptions from a sandboxed template.
  • The new torrent seeds from hardlinks in an upload folder, with the file names you chose, so the library keeps its own names.
  • A watched folder for releasers: whatever lands there starts an upload on its own up to the decision, then moves to the releases folder once it seeds.
  • Packs from episodes picked by hand: episodes downloaded one at a time, even ones already seeding with their own torrent, become a season pack or a complete pack.

Fits your setup

  • Several torrent clients at once (qBittorrent, qui), UNIT3D trackers, optional Radarr and Sonarr.
  • Works without a media folder too, just for your torrents and uploads.
  • Plugins, signed webhooks and API keys to extend it (docs/SDK.md).
  • English and Italian interface, and a guided tour that sets everything up at the first access.

Who it is for

  • You seed from hardlinks of your library and want to know, at a glance, what seeds and what doesn't.
  • You lost your torrents (a reinstall, a moved disk, a client wiped) and want the library to seed again without downloading anything.
  • Your seeding folder keeps growing and you want to know what can go, and whether it's safe to remove.
  • You release or re-upload and want naming, dupe checks, screenshots and seeding handled in one flow, across several trackers.

Install

Docker Compose

services:
  nazgarr:
    image: ghcr.io/lktorrentz/nazgarr:stable
    container_name: nazgarr
    restart: unless-stopped
    ports:
      - "8080:8080"
    environment:
      - PUID=1000            # the user that owns your media and torrent folders
      - PGID=1000
      - TZ=Europe/Rome
      - APP_SECRET_KEY=change-me   # see below
    volumes:
      - ./config:/app/config # config.yaml, database, cache
      - /srv/data:/data      # torrents/ and media/, the same path your torrent client sees

Generate APP_SECRET_KEY once and keep it with your backups. It encrypts the stored credentials (tracker tokens, client passwords, API keys), and without it they can't be read:

openssl rand -base64 32 | tr '+/' '-_'

Then docker compose up -d and open http://<host>:8080. Building from source instead: clone the repository and run docker compose up -d with the docker-compose.yml in it, which reads the key from a .env file.

Unraid

A template is in unraid/nazgarr-template.xml. Save it to /boot/config/plugins/dockerMan/templates-user/, then pick it under Docker › Add Container › Template. It uses the :stable image, 99:100 as the user, and mounts /mnt/user/data as /data, like the TRaSH Guides layout.

Python package (no Docker)

Every stable release is also on PyPI, with the web UI already built inside, so neither Docker nor Node is needed. You need Python 3.12 or newer, pipx, mediainfo and ffmpeg:

sudo apt install pipx mediainfo ffmpeg      # Debian/Ubuntu (macOS: brew install pipx media-info ffmpeg)
pipx install nazgarr
nazgarr init --scan-root /mnt               # the folder your disks live under
nazgarr serve                               # or install it as a service, below

init writes config.yaml and a secret key (secret.key, readable only by you) to ~/.config/nazgarr (macOS: ~/Library/Application Support/Nazgarr). The database goes in ~/.local/share/nazgarr. To start it at boot:

nazgarr install-service
systemctl --user daemon-reload && systemctl --user enable --now nazgarr        # Linux (systemd)
sudo loginctl enable-linger $USER                                              # Linux: start without logging in
launchctl load -w ~/Library/LaunchAgents/io.github.lktorrentz.nazgarr.plist    # macOS (launchd)

Run it as the user that owns your media and torrent folders, and as a single process (the upload worker and the scheduler live inside it). Update with pipx upgrade nazgarr. Windows is not tested yet.

Release channels

Tag What it is
:stable (also :latest) Releases promoted by hand after testing. Use this one. The Python package follows it.
:nightly A test build for every push to main. It moves fast and may break.
:X.Y.Z Every published version, pinned.

Configuration › Application › Check for updates follows the channel you're on.

Basic configuration

Nazgarr needs very little up front. Disks, folders, clients, trackers and thresholds are set from the web UI and saved in its database, with no restart.

Port: 8080. Map it to whatever you like on the host ("9000:8080"). The Python package takes nazgarr serve --host 0.0.0.0 --port 8080.

Environment

Variable
APP_SECRET_KEY Required. Encrypts the stored credentials. Keep it with your backups.
PUID / PGID The user and group the app runs as. They need write access to your folders, because hardlinks are created there. Default 1000.
TZ Time zone for the schedule and the logs.
NAZGARR_SETUP_CODE Optional: your own one-time code to create the account (otherwise one is generated and printed in the log).

config/config.yaml is created on the first start with these defaults. It holds the only two settings that need a restart:

disk_scan_root: /data      # where the disks are, inside the container
data_dir: /app/config/data # database and cache

Disks and paths. Each disk is a folder under disk_scan_root that holds the torrents and the media of one filesystem. Hardlinks only work inside one filesystem, so a disk's folders must all be on the same one. A disk can have several seeding folders and several media folders (say movies/ and tv/ side by side, or a separate cross-seed folder). With the TRaSH Guides layout there is a single disk:

/data
├── torrents/      seeding folder (what your torrent client downloads into)
│   └── uploads/   optional: where your uploads seed
├── media/
│   ├── movies/
│   └── tv/
└── releases/      optional: the watched folder for your own releases

With several separate disks, mount each one under a common parent and point disk_scan_root at it:

    volumes:
      - ./config:/app/config
      - /mnt/disk1:/mnt/disk1
      - /mnt/disk2:/mnt/disk2
# config.yaml: disk_scan_root: /mnt

Every subfolder of disk_scan_root shows up in Configuration › Storage, ready to be added as a disk.

Same paths as your torrent client. Mount the data folder at the same path in Nazgarr and in your client (for example /data in both), and it just works. If the client sees a disk elsewhere (say /downloads), set that path for the disk in the client's settings.

First access

  1. Create the account. Nazgarr stays closed until its single account exists. The log prints a one-time setup code (docker logs nazgarr, or the service log). Open the web UI, enter the code, and choose a username and password.

  2. Follow the guided setup. A short welcome asks whether you upload and whether you use Radarr/Sonarr. The tour then walks you through each screen:

    • disks and their folders;
    • torrent clients;
    • the TMDB API key (free);
    • your trackers, each with its API token;
    • for uploads only, image hosts and naming.

    A checklist on the dashboard follows what you have configured.

  3. Run the first scan. It only reads: nothing is linked, added or moved. Then the tour shows you around the views. From there, scans run on the schedule you set.

API and automation

Everything the UI does goes through a JSON API under /api. The interactive reference is at http://<host>:8080/docs. Scripts authenticate with an API key created in Configuration › API keys. See docs/SDK.md for plugins, webhooks and API keys, and examples/nazgarr-ntfy for a complete plugin.

Development

python3 -m venv .venv
./.venv/bin/pip install -r requirements.txt -r requirements-dev.txt
cp config.example.yaml config.yaml          # point disk_scan_root/data_dir at local folders
export APP_SECRET_KEY=$(openssl rand -base64 32 | tr '+/' '-_')
./.venv/bin/uvicorn nazgarr.main:app --reload --port 8080

cd frontend && npm install && npm run dev   # http://localhost:5173, proxies /api to the backend

Checks, the same as CI: ruff check . and pytest -q for the backend, npm run lint, npx tsc -b and npx vitest run in frontend/. Python dependencies are locked with hashes: edit requirements.in, then run uv pip compile requirements.in --python-version 3.12 --generate-hashes -o requirements.txt.

Design decisions live in docs/SPEC.md, the plan in docs/ROADMAP.md, the schema in docs/schema.sql, and the frontend notes in frontend/README.md. Issues and pull requests are welcome. If something in the spec looks wrong, open an issue before working around it.

License

GPL-3.0

Metadata

Release files for nazgarr 0.7.5

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for nazgarr 0.7.5
File Size Uploaded
nazgarr-0.7.5.tar.gz 1.3 MB Details

Built distribution (wheel)

Table of built distributions (wheels) for nazgarr 0.7.5
File Interpreter ABI Platform
nazgarr-0.7.5-py3-none-any.whl Python 3 none any Details

Total release size: 2.5 MB

Release files / nazgarr-0.7.5.tar.gz

Download URL nazgarr-0.7.5.tar.gz
Size 1.3 MB
Tags Source
SHA-256 checksum
How to use checksums
c2292ea30c8d8861aef56843c8796b1d833c58ebc231cd2bb4d12d4f5edaea41
BLAKE2b-256 checksum
How to use checksums
8f6565bedc1a1f20006beaf33206bcffedbd7dc8530b8d9b79fb85f29f855d7e
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Oct 3, 2026.

Transparency log

Release files / nazgarr-0.7.5-py3-none-any.whl

Download URL nazgarr-0.7.5-py3-none-any.whl
Size 1.2 MB
Tags Python 3
SHA-256 checksum
How to use checksums
a77f440e9b2df6ea362d3b5915be4fdd10466a37974930eff8d0270b7f24ee32
BLAKE2b-256 checksum
How to use checksums
caa61f28eb2c1ad2122021e514b8654e5e3e41b52604d430ef9d04179a30f067
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Oct 3, 2026.

Transparency log

Release history Release notifications | RSS feed

0.8.9

2 release files

0.8.5

2 release files

0.8.2

2 release files

0.8.0

2 release files

This release

0.7.5 This release

2 release files

0.7.2

2 release files

0.7.0

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page