Home Docs GitHub

Documentation

Everything you need to deploy, configure, and extend Crate.

Overview

Crate is a self-hosted music manager for collectors. It connects to music metadata providers (MusicBrainz, Deezer, or your own) to search and browse artists, then downloads tracks from Soulseek via slskd. Downloaded files are automatically organized, tagged with full metadata and cover art, and placed in your library.

The stack is a Go backend with SQLite, a React frontend, and standalone gRPC provider binaries — all shipped in a single Docker image.

crate (main process) ├── HTTP API + React SPA ├── Provider Manager │ ├── gRPC → provider-musicbrainz (:50051) │ ├── gRPC → provider-deezer (:50052) │ └── gRPC → custom external provider ├── Scheduler (new releases, auto-queue, quality upgrades) ├── Downloader (slskd integration) ├── Organizer (file moves + naming template) ├── Tagger (ID3/FLAC metadata + cover art) └── Navidrome notifier (optional library scan trigger)

Deployment

The recommended deployment is via Docker Compose with slskd as a sidecar. Crate needs a Soulseek account (provided through slskd) to download music.

# docker-compose.yml
services:
  crate:
    image: ghcr.io/theoutdoorprogrammer/crate:latest
    ports:
      - "6969:6969"
    volumes:
      - ./crate:/app/data             # SQLite databases
      - ./slskd/downloads:/app/downloads  # Must match slskd's download dir
      - ./library:/app/library       # Organized music library
    environment:
      - CRATE_SLSKD_URL=http://slskd:5030
      - CRATE_SLSKD_API_KEY=your_api_key
    depends_on:
      - slskd
    restart: unless-stopped

  slskd:
    image: slskd/slskd:latest
    ports:
      - "5030:5030"
    volumes:
      - ./slskd:/app                  # slskd data (config, downloads)
      - ./library:/music             # Share your library on Soulseek
    environment:
      - SLSKD_REMOTE_CONFIGURATION=true
      - SLSKD_API_KEY=your_api_key
      - SLSKD_SOULSEEK_USERNAME=your_username
      - SLSKD_SOULSEEK_PASSWORD=your_password
    restart: unless-stopped
Important: Crate's downloads volume (/app/downloads) must point to the same host directory that slskd writes completed downloads to. With the config above, slskd stores downloads under ./slskd/downloads/ and crate reads from the same path. If these don't match, crate won't find downloaded files.
Tip: The CRATE_SLSKD_API_KEY must match the SLSKD_API_KEY you set for slskd. These two services communicate over the slskd REST API.

Volumes

Three volume mounts are important:

Authentication

Crate does not include built-in authentication. It's designed to run behind your existing auth layer — a reverse proxy with basic auth, Authelia, Authentik, Cloudflare Access, a VPN, or whatever you prefer. If it's only accessible on your local network, no auth may be necessary.

Environment Variables

These are set at startup and control core paths, ports, and provider configuration. All have sensible defaults — only CRATE_SLSKD_API_KEY is required.

Variable Default Description
CRATE_PORT 6969 HTTP port the web UI and API listen on.
CRATE_DB_PATH ./crate.db Path to the main SQLite database.
CRATE_CACHE_PATH ./cache.db Path to the provider cache database. Stores cached search results and metadata with a configurable TTL.
CRATE_ACTIVITY_PATH ./activity.db Path to the activity log database. Records download events, errors, and system events.
CRATE_DOWNLOADS_DIR ./downloads Directory where slskd places completed downloads. Crate reads from here.
CRATE_LIBRARY_PATH ./library Directory where organized music files are placed. Layout is controlled by the naming template.
CRATE_SCAN_INTERVAL 6h How often the scheduler runs (new release detection, auto-queue, quality upgrades). Accepts Go duration strings: 30m, 6h, 24h, etc.
CRATE_SLSKD_URL http://localhost:5030 Base URL of your slskd instance.
CRATE_SLSKD_API_KEY none API key for slskd. Required for downloads to work.
CRATE_PROVIDERS musicbrainz:./provider-musicbrainz:50051,deezer:./provider-deezer:50052 Comma-separated list of providers. Format: name:binary:port. See Providers.
CRATE_MB_USER_AGENT Crate/0.1.0 ... User-Agent string for MusicBrainz API requests. MusicBrainz requires a descriptive User-Agent.

UI Settings

These settings are stored in the database and configurable from the Settings page in the web UI. Changes take effect immediately without restarting.

Setting Default Description
provider_primary musicbrainz Default provider for search and browse. Can be switched on the fly from the search UI.
cache_ttl_hours 24 How long provider search results and metadata are cached before re-fetching.
quality_tiers FLAC > MP3 320 > MP3 256 Priority-ordered list of acceptable download formats. The downloader picks the highest-tier match from available sources. Stored as JSON.
quality_fallback_enabled true Whether to accept files outside configured quality tiers. When disabled, only files matching a tier are downloaded.
negative_keywords [] JSON array of keywords (e.g. ["acapella", "instrumental"]). Files matching any keyword are skipped during auto-download. Manual search still shows them, marked as negative matches.
naming_template {artist}/{album} ({year})/{track:2} - {title} Folder/file layout for new downloads. See Naming Template. Empty = default.
max_concurrent_slskd 10 Maximum number of simultaneous downloads from Soulseek.
shadow_ban_duration_minutes 60 How long to temporarily block a user after failed or stale downloads. Expired bans are cleaned up automatically.
max_auto_queue 50 Maximum tracks the scheduler will auto-queue per cycle. Prevents flooding the download queue.
requeue_cooldown_days 7 Minimum days before a failed track can be re-queued by the scheduler.
activity_retention_days 30 How long activity log entries are kept before automatic cleanup.
scan_interval from env Override the scheduler interval at runtime (same format as CRATE_SCAN_INTERVAL).
slskd_url from env Override the slskd URL at runtime.
slskd_api_key from env Override the slskd API key at runtime. Treated as sensitive (masked in the UI).
library_path from env Override the library path at runtime.
download_format_preference none Preferred download format when multiple options are equivalent in quality tier.
navidrome_url none Base URL of your Navidrome instance. See Navidrome Integration.
navidrome_user none Navidrome username for triggering library scans.
navidrome_password none Navidrome password. Treated as sensitive (masked in the UI).
Note: Settings like slskd_url, slskd_api_key, library_path, and scan_interval can be set via environment variables at startup or overridden in the UI. The UI value takes precedence when set.

Naming Template

The folder and file layout for downloads is a template you can change from Settings → Library, with a live preview as you type. Use it to match an existing library convention (e.g. one served by Navidrome) so Crate's downloads slot in cleanly. The default matches Crate's original layout:

{artist}/{album} ({year})/{track:2} - {title}
Token Meaning
{artist} / {albumartist} Artist name. Crate tracks album artists, so these are identical — {albumartist} exists for familiarity with Lidarr/beets templates.
{album} Album title.
{year} Album release year. Renders empty when unknown.
{track} Track number. Zero-pad with a width: {track:2} renders 6 as 06.
{disc} Disc number. Supports padding. Renders empty when unknown.
{title} Track title.

Example: to match the common Artist/Artist - Year - Album convention:

{artist}/{artist} - {year} - {album}/{track:2} {title}

Behavior

Providers

Providers supply music metadata (artist search, discography browsing, album/track details, cover art). Crate ships with two built-in providers and supports adding custom ones via gRPC.

Built-in Providers

Provider Source Rate Limit Notes
MusicBrainz MusicBrainz.org API 1 req/s Open database. Best for completeness and accuracy. Default provider.
Deezer Deezer public API 10 req/s Faster browsing, good cover art. Useful as a secondary provider.

How Providers Work

Provider Configuration

The CRATE_PROVIDERS environment variable defines which providers to load. Format:

# Built-in providers (binary path relative to crate binary)
CRATE_PROVIDERS=musicbrainz:./provider-musicbrainz:50051,deezer:./provider-deezer:50052

# External provider (network address instead of binary path)
CRATE_PROVIDERS=musicbrainz:./provider-musicbrainz:50051,spotify:external:192.168.1.10:50053

Built-in providers are started as child processes by Crate. External providers connect to a remote gRPC server you run separately.

Download Flow

When you watch an artist, album, or track, here's what happens:

  1. Queuing — watched items are saved with status wanted. The scheduler checks for new wanted tracks every scan interval.
  2. Search — Crate searches slskd (Soulseek) for each wanted track, scoring all results through a unified scoring system.
  3. Selection — the highest-scoring result is selected. Blacklisted files, shadow-banned users, and files outside configured tiers (when fallback is disabled) are excluded.
  4. Download — the file is downloaded via slskd. Track status changes to downloading. The downloader checks progress every 10 seconds.
  5. Organize — completed downloads are moved into the library using the configurable naming template.
  6. Tag — metadata (artist, album, track name, track number, year) and cover art are embedded into the file.
  7. Notify — if Navidrome is configured, a library scan is triggered. Track status becomes owned.

Retry, Blacklist & Shadow Bans

Not every download succeeds on the first try. Crate handles failures automatically:

Concurrency

The max_concurrent_slskd setting (default 10) limits how many downloads run simultaneously. The max_auto_queue setting (default 50) caps how many tracks the scheduler adds per cycle, preventing the queue from growing unbounded.

Scoring System

Every search result is scored through a unified pipeline that balances quality, correctness, and availability. The highest-scoring file wins.

Component Points Description
Quality tier 25 – 100 Based on your configured tier list. Tier 1 = 100, Tier 2 = 75, Tier 3 = 50, etc. Files outside configured tiers use a fallback score (capped below the lowest tier).
Artist match +20 Bonus if the artist name appears in the filename. Increases confidence it's the correct file. Intentionally kept below the tier gap (25) so quality always dominates.
Free upload slot +10 Bonus if the user has a free upload slot, meaning the download can start immediately.
Queue length 0 – 15 Inverse decay: 15 / (1 + queueLength). Empty queue = 15 points. Queue of 5 = 2 points. Longer queues approach zero.

The title must appear in the filename (hard filter). All bonuses combined (max 45) can overcome one quality tier gap but never two, ensuring your quality preferences are respected while still favoring available, correctly-labeled sources.

Quality fallback: When quality_fallback_enabled is set to false, files that don't match any configured tier are rejected entirely. When enabled (default), they receive a reduced score below the lowest tier.
Negative keywords: Configure keywords like acapella or instrumental in Settings. Files matching any keyword are automatically skipped during scheduled downloads. Manual search still returns them (dimmed, labeled "negative keyword") so you can override when needed.

Quality Upgrades

Crate continuously improves your library quality. The quality system works as follows:

  1. Quality tiers are priority-ordered in settings (default: FLAC > MP3 320 > MP3 256). Higher in the list = better.
  2. When a track is downloaded, its format and bitrate are recorded.
  3. The scheduler scans one artist per day (round-robin), checking if any owned tracks can be upgraded to a higher quality tier.
  4. Upgradeable tracks are re-queued automatically. The new download replaces the old file.

The requeue_cooldown_days setting (default 7) prevents the scheduler from re-queuing the same track too frequently if better quality isn't available yet.

Lidarr API Compatibility

Crate includes a Lidarr v1 API compatibility shim at /api/v1/, allowing iOS and Android apps built for Lidarr to manage your Crate library. No extra configuration needed — point the app at your Crate URL with any API key and it works.

Tested with: Helmarr (iOS) — full artist/album management, search, and monitoring all work out of the box. Interactive search is not supported — use the Crate UI for manual file selection.

Concept Mapping

Lidarr and Crate model things differently. The shim translates between them transparently:

Lidarr Concept Crate Equivalent Notes
Monitor "all" Artist status watched Full discography tracked.
Monitor "latest" Watch newest album + enable new releases Most recent album by year, future albums auto-added.
Quality profile "Crate Quality" Crate uses priority-ordered quality tiers instead of Lidarr-style profiles.
Metadata profile Active provider name Shows whichever provider is set as primary (MusicBrainz, Deezer, etc.).
Root folder CRATE_LIBRARY_PATH Library directory with real disk usage stats.
Monitored album Album status watched Tracks set to wanted.
Unmonitored album Album status ignored Tracks cascade to ignored, preserving owned and downloading states.
ArtistSearch command Queue all wanted tracks Same as "Search wanted tracks" in the Crate UI.
AlbumSearch command Queue album's wanted tracks Same as searching from the album page.
"Search for missing" on add Auto-queue after watch Queues all wanted tracks immediately when enabled.

Supported Endpoints

The shim implements the Lidarr v1 endpoints that mobile apps typically use:

Authentication

The shim accepts any API key in the X-Api-Key header or apikey query parameter. Crate has no built-in auth — use a reverse proxy if you need access control.

Contributing

The Lidarr shim covers the most common endpoints. If your app needs an endpoint that isn't implemented yet, contributions are welcome — the shim is a single file at internal/api/lidarr.go.

Library Import

Crate can adopt a music collection it didn't download. Settings → Library → Import Existing Library scans your library folder, reads embedded tags, and records everything as owned — making Crate a drop-in manager for an established Navidrome/media-server setup rather than a fresh-start tool.

How it works

Formats: the importer reads MP3 and FLAC — the same formats Crate writes tags to. For other formats, the database schema is documented for custom importers (see Database).

Crate can trigger a Navidrome library scan after each successful download, so new music appears in your streaming library immediately.

Setup

Configure these three settings in the Crate UI (Settings page):

Setting Example
navidrome_url http://navidrome:4533
navidrome_user admin
navidrome_password your_password

All three fields are required. If any are empty, the Navidrome integration is silently disabled. Crate authenticates via the Subsonic API token+salt scheme.

Tip: Point both Crate's CRATE_LIBRARY_PATH and Navidrome's music folder to the same directory (or use a shared volume). Crate organizes files using the naming template, which you can set to match an existing library convention, and Navidrome indexes them automatically.

Supported Formats

Format Extension Type
FLAC.flacLossless
WAV.wavLossless
MP3.mp3Lossy
Ogg Vorbis.oggLossy
Opus.opusLossy
AAC.aacLossy
MPEG-4 Audio.m4aLossy

The downloader recognizes all these formats when scoring search results. The tagger embeds metadata into FLAC (Vorbis comments) and MP3 (ID3v2) files. Cover art is embedded in both formats.

API Reference

Crate exposes a REST API on the same port as the web UI. All endpoints return JSON.

Status

MethodPathDescription
GET/api/statusHealth check and system status.

Search & Browse

MethodPathDescription
GET/api/search?q=&limit=&offset=Search for artists via the active provider. Paginated.
GET/api/browse/artist/{id}Browse an artist's full discography from the provider.
GET/api/browse/album/{id}Browse an album's track listing from the provider.

Library

MethodPathDescription
GET/api/library/search?q=Search your local library.
GET/api/artistsList all watched/owned artists.
GET/api/artists/{id}Get artist details with albums.
DELETE/api/artists/{id}Remove artist and all associated data.
GET/api/albums/{id}Get album details with tracks.
DELETE/api/albums/{id}Remove album.
GET/api/tracks/{id}Get track details.
DELETE/api/tracks/{id}Remove track.

Watching

MethodPathDescription
POST/api/watch/artistWatch an artist (full discography tracking).
POST/api/watch/albumWatch a specific album.
POST/api/watch/trackWatch a specific track.

Downloads

MethodPathDescription
GET/api/downloadsList current download queue.
POST/api/downloads/{id}/retryRetry a failed download.
DELETE/api/downloads/{id}Cancel/remove a download.

Providers

MethodPathDescription
GET/api/providersList all configured providers with health status.
POST/api/relink/artist/{id}Relink an artist to a different provider.
POST/api/relink/album/{id}Relink an album to a different provider.
POST/api/relink/track/{id}Relink a track to a different provider.

Blocked Sources

MethodPathDescription
GET/api/blacklistList all permanently blacklisted username+file pairs.
DELETE/api/blacklist/{id}Remove a blacklist entry (allows re-downloading from that source).
GET/api/cooldownsList active shadow bans (temporary per-user blocks).
DELETE/api/cooldowns/{id}Remove a shadow ban (immediately unblocks the user).

Settings & Activity

MethodPathDescription
GET/api/settingsGet all settings (sensitive values masked).
PUT/api/settingsUpdate settings. Rejects invalid naming_template values.
GET/api/settings/naming-preview?template=Render a naming template against sample metadata. Empty = default template.
POST/api/library/importStart a library import. Body: {"path": "...", "dry_run": true} (both optional; path defaults to the library dir). Returns 409 if one is already running.
GET/api/library/importImport status: state, progress, and the final report.
GET/api/activity?limit=&offset=Get activity log. Paginated.
DELETE/api/cacheClear the provider cache.

Custom Providers

You can build your own metadata provider by implementing the gRPC service defined in proto/provider/provider.proto. The service interface includes:

Run your provider as a standalone gRPC server and add it to the CRATE_PROVIDERS env var:

CRATE_PROVIDERS=musicbrainz:./provider-musicbrainz:50051,my-provider:external:my-host:50053

Use the external keyword as the binary path to tell Crate it should connect to a remote server instead of launching a child process. Community-built providers and the database schema are documented in DATABASE.md.

Database

Crate uses three SQLite databases:

The full database schema is documented in DATABASE.md in the repository.