- B
So What
DSub · 2m ago
- T
Take Five
Symfonium · 14m ago
- G
Goodbye Pork Pie Hat
Feishin · 31m ago
Self-hosted · Open source · MIT
Every play,counted.
Navidrome Stat polls the getNowPlaying API of your Navidrome servers and turns it into one self-hosted dashboard — listening time, trends, rankings, and a year in review. Your data never leaves your machine.
Blue in Green
Miles Davis — Kind of Blue
Everything your listening history knows
Navidrome clients don’t share stats with each other. Navidrome Stat watches the server instead — one history, every client.
Every client, one history
DSub, Symfonium, Feishin, web players — plays from any Subsonic client land in the same history. Multiple Navidrome servers, too.
Charts that go deep
Hourly and daily trends, a weekday-by-hour heatmap, client usage, transcoding rates, and artist, album, and track rankings.
A year in review
Totals, listening streaks, and your top artists, albums, and tracks — your own Wrapped, on your own data.
Cover art, proxied and cached
Album art flows through an authenticated, size-capped cache — your library stays private.
Your data, your rules
Everything lives in one SQLite file you own. Per-user export, import, and deletion. Optional token auth. No telemetry.
Ten themes
Catppuccin, Nord, Dracula, Tokyo Night, Gruvbox, Solarized — instant switch.
Five languages
English, 简体中文, 繁體中文, 日本語, Deutsch — more on the roadmap.
Pick a theme, any theme
Ten built-in themes recolor every tab instantly. Click one and watch the dashboard reskin:
Your year in music
One page, your whole year: totals, streaks, and the songs that got you through it.
- 0
- plays
- 0
- hours listened
- 0
- artists
- 0
- day streak
Top tracks
Blue in Green
Sample data — the real numbers come from your own library.
How it works
No client plugins, no accounts, no cloud. One small container next to your music server.
- 1
Polls getNowPlaying
Every 10 seconds (configurable), the collector asks each Navidrome server what’s playing — via the Subsonic API you already run.
- 2
Tracks real sessions
Pauses and gaps are excluded. A track counts as played once active listening crosses your threshold — 30 seconds by default.
- 3
Stores and serves
Qualified plays land in SQLite on your disk and render in a self-contained dashboard at port 39421.
When a server advertises the OpenSubsonic playbackReport extension, it is used automatically for sharper position and duration tracking.
Up in one compose file
Copy, paste, and open port 39421. Pin a version tag for reproducible updates.
services:
navidrome-stat:
image: stepaniah/navidrome-statistic:v0.8.1
container_name: navidrome-stat
user: "1000:1000"
ports:
- "39421:39421"
volumes:
- navidrome-stat-data:/data
environment:
NAVIDROME_URL: ${NAVIDROME_URL}
NAVIDROME_USER: ${NAVIDROME_USER}
NAVIDROME_PASS: ${NAVIDROME_PASS}
STATS_API_TOKEN: ${STATS_API_TOKEN}
DATABASE_URL: /data/navidrome_stats.db
restart: unless-stopped
volumes:
navidrome-stat-data: docker run -d --name navidrome-stat \
-p 39421:39421 \
-e NAVIDROME_URL=https://navidrome.example.com \
-e NAVIDROME_USER=you \
-e NAVIDROME_PASS=change-me \
-e STATS_API_TOKEN=$(openssl rand -hex 24) \
-v navidrome-stat-data:/data \
stepaniah/navidrome-statistic:v0.8.1 NAVIDROME_URL=https://navidrome.example.com
NAVIDROME_USER=example_user
NAVIDROME_PASS=change-me
STATS_API_TOKEN=use-a-long-random-value With STATS_API_TOKEN set, enter it once on the login screen — the browser keeps an HttpOnly session cookie, never the token.
Full configuration referencePrivate by default
-
Data stays local
Listening history lives in one SQLite file inside your own volume.
-
Export or delete anytime
Per-user JSON export, import, and deletion are built into the settings page.
-
Optional auth
Set STATS_API_TOKEN and the dashboard and APIs require login — sessions use an HttpOnly cookie.
-
No telemetry
The container talks to exactly one outside party: your Navidrome servers.
Honest limitations
-
One instance per set of sources
Multiple instances polling the same servers would double-count plays.
-
Plaintext SQLite
The database and credentials saved via settings are stored unencrypted — protect the volume like any secret.
-
No built-in TLS
For remote access, put it behind your HTTPS reverse proxy.
Questions, answered
Which music clients does it support?
Any Subsonic-compatible client — DSub, Symfonium, Feishin, play:Sub, substreamer, the Navidrome web UI, and more. Statistics come from the server’s getNowPlaying endpoint, so nothing needs to be installed client-side.
Can I track multiple Navidrome servers?
Yes. Add each server under Settings → Connections; plays are tagged per source and the dashboard can filter by server.
How exactly is a play counted?
Accumulated active listening time must cross PLAY_THRESHOLD_SEC (30 seconds by default). Paused and missing intervals are excluded, checkpoints update the same record, and sessions that end below the threshold are kept as attempts — not plays.
Does it work with Jellyfin or Airsonic?
It works with any server that speaks the Subsonic API, but it is developed and tested against Navidrome. OpenSubsonic extensions are used when a server offers them.
How is this different from Last.fm or Maloja?
Last.fm records scrobbles in someone else’s cloud; Maloja is self-hosted but still depends on clients submitting scrobbles. Navidrome Stat needs no client support at all — it watches the server’s own now-playing feed, so every Subsonic client is covered by default, and the data stays on your disk.
Where does my data live?
In a single SQLite file on your own disk, inside the container’s /data volume. You can export it as JSON, import it elsewhere, or delete a user’s history — all from the settings page.
How heavy is it?
One small container, one SQLite file, a 10-second poll loop. A Raspberry Pi handles it comfortably.