Symlinkarr turns Real-Debrid-backed media into a clean local library.
It scans your source mount, matches files to ID-tagged movie and series folders, writes stable symlinks, and keeps track of them in SQLite. It works with plain folders or alongside Plex, Emby, and Jellyfin.
If your current stack looks like "RD mount + Sonarr/Radarr + a messy library full of stale or misplaced links", Symlinkarr is the cleanup and repair layer between the mount and your media server.
| CLI usage | Web UI |
|---|---|
![]() |
![]() |
- scans RD-backed mounts and local library folders
- matches against
{tvdb-*}and{tmdb-*}tagged folders - creates and updates symlinks in a repeatable way
- repairs dead links and finds missing content
- audits bad, stale, or misplaced links before cleanup
- can refresh Plex, Emby, and Jellyfin after link changes
- avoids hammering your media server after large cleanup or repair runs
No media server is required.
- Real-Debrid-backed mounts such as Zurg and Decypharr
- Sonarr and Radarr
- Prowlarr
- Bazarr
- Tautulli
- TMDB and TVDB
- Debrid Media Manager
- Plex, Emby, and Jellyfin, optionally and together
README examples use symlinkarr ... as the normal command form.
- release binary install: run
./symlinkarr ...orsymlinkarr ...if it is on yourPATH - source checkout: run
cargo run -- ... - Docker: mainly for long-running daemon mode via
docker compose up -d
Example:
symlinkarr scan --dry-run
cargo run -- scan --dry-runDownload a release tarball from GitHub Releases, or build locally with Cargo, or run with Docker.
Release binary example:
tar -xzf symlinkarr-<version>-linux-amd64.tar.gz
cd symlinkarr-<version>-linux-amd64
./symlinkarr --helpStart from config.example.yaml.
Minimum:
- one or more library paths
- one or more source paths
- a writable SQLite
db_path - TMDB and TVDB credentials if you want full metadata matching
For a fresh local install, run symlinkarr bootstrap to create a starter config and directories. Docker users can skip this — compose already mounts config and data.
To restore from an existing backup into a fresh install: symlinkarr restore <path-to-backup.json>. Works without a config file — useful for both local and Docker fresh installs (e.g. docker exec symlinkarr symlinkarr restore /app/backups/<file>.json).
From source:
cargo run -- config validate --output json
cargo run -- doctor --output jsonFrom a release binary:
./symlinkarr config validate --output json
./symlinkarr doctor --output jsonFrom source:
cargo run -- scan --dry-run
cargo run -- scan
cargo run -- webFrom a release binary:
./symlinkarr scan --dry-run
./symlinkarr scan
./symlinkarr webWeb UI default: http://127.0.0.1:8726
For a host-local daemon install, keep Symlinkarr in its own compose file and mount the real host paths directly.
Example local stack:
services:
symlinkarr:
image: ghcr.io/lap87/symlinkarr:rc
container_name: symlinkarr
restart: unless-stopped
user: "1000:1000"
command: ["--config", "/app/config/config.yaml", "daemon"]
extra_hosts:
- "host.docker.internal:host-gateway"
volumes:
- ./config.docker.yaml:/app/config/config.yaml:ro
- ./data:/app/data
- /mnt:/mnt:rslave
- /path/to/secrets:/app/secrets:roTypical local operations:
docker compose -f docker-compose.local.yml up -d
docker compose -f docker-compose.local.yml logs -f symlinkarr
docker exec symlinkarr symlinkarr --config /app/config/config.yaml status --output json
docker stop symlinkarrThe floating rc tag follows the newest release candidate. Stable installs should use
latest; exact-version tags remain available when a fixed deployment is preferred.
If you are replacing another standalone tool, stop that container separately instead of editing its compose file unless both services truly belong to the same stack.
Symlinkarr is meant to run like most home *arr apps: local by default, locked down if you expose it.
local-only: bind to127.0.0.1, keepallow_remote: false, and use the UI/API from the same host or through your own tunnel/reverse proxy.remote UI: bind beyond loopback, setallow_remote: true, and configureweb.username+web.password.scripts/API: optionally addweb.api_keyfor scripts that should useAuthorization: Bearer ...orX-API-Key.
Practical rules:
local-onlyis trusted mode: no built-in auth is requiredweb.username+web.passwordprotect the HTML UI and API with HTTP Basic authweb.api_keyprotects JSON API clients viaAuthorization: Bearer ...orX-API-Key- when the UI is exposed remotely, browser forms use a session cookie plus CSRF token
- remote UI access requires
web.username+web.password; an API key alone is not enough for the built-in HTML UI
Recommended default:
- keep the built-in UI loopback-only unless you have a specific reason to expose it
Symlinkarr keeps TMDB/TVDB metadata cached for a long time on purpose.
- metadata is mostly stable, while API timeouts and rate limits are expensive
- if one title looks wrong, refresh that cache entry instead of clearing everything
- short global TTLs mostly create extra API calls
Useful commands:
symlinkarr cache invalidate tmdb:12345symlinkarr cache invalidate tmdb:tv:symlinkarr cache invalidate anime-listssymlinkarr cache clear
Symlinkarr backups keep the important app state together:
- a JSON manifest of active symlinks for normal restore flows
- a sibling SQLite snapshot from
backup create, so you can recover the database too - app-state snapshots for the active
config.yamland anysecretfile:-backed secrets the current install can see
Use the backup names deliberately:
Symlinkarr Backup: the main backup to keep for day-to-day recoveryRestore Point: a lighter rollback point created around repair, cleanup, or daemon work
This is closer to a real app backup now, but not identical to Sonarr/Radarr yet:
- restoring a
Symlinkarr Backupfrom an already configured install can write link state, SQLite state, config, andsecretfile:secrets back to the current install - standalone/no-config restore only recreates secrets inside the config tree or the standard Docker
/app/secretslayout; other external secret paths still need to be recreated manually - secrets that only exist as environment variables still live outside the backup set
- a truly fresh install still needs its config/secrets placed before first startup
Current-format backups are checked during backup list and backup restore, so corrupted or tampered backups fail loudly instead of half-restoring.
Restore also stays confined to the configured backup.path, so a mistyped absolute path or symlink escape cannot make the restore flow read arbitrary files outside the backup directory.
When old scheduled backups rotate out, their paired .sqlite3 snapshots and app-state bundles are removed with them so retention limits still bound disk usage.
symlinkarr scan --dry-run
symlinkarr scan --library Anime --search-missing
symlinkarr backfill --arr sonarr-anime --dry-run
symlinkarr backfill --arr all --search-missing
symlinkarr backfill --arr sonarr-anime --item 86-eighty-six --dry-run --search-missing
symlinkarr status --health
symlinkarr status --health --output json
symlinkarr cleanup audit --scope anime
symlinkarr cleanup prune --report <REPORT.json>
symlinkarr repair auto --dry-run
symlinkarr discover list
symlinkarr import --source /mnt/rd --movie-destination /library/movies --tv-destination /library/tv --anime-destination /library/anime --content-type auto --mode preview
symlinkarr cache status
symlinkarr cache invalidate tmdb:12345
symlinkarr cache invalidate tmdb:tv:
symlinkarr webIf you are running from a source checkout, prepend cargo run -- to the same commands.
Advanced note: cleanup remediate-anime is for older anime libraries with mixed roots or Plex Hama AniDB/TVDB duplicates. Most setups do not need it.
Discover note: discover list previews where source files would land for tagged folders that still look empty or underlinked. Web/API discover is read-only for now.
Import note: import is for bootstrap/recovery when you already have RD/DMM/provider content but not the matching local library folders or Symlinkarr DB. See Provider Source Import.
- keep a Real-Debrid-backed library usable without hand-sorting files
- keep Sonarr/Radarr-style ID-tagged folders clean
- detect and repair bad symlinks before Plex, Emby, or Jellyfin drift too far
- clean up legacy linked libraries
- anime specials without usable anime-lists numbering hints may still need manual search terms, because many indexers are weak at
S00Exx-style anime queries

