Sign inSign up

passport888/navidrome-telegram

By passport888

Updated 8 days ago

Image
1

316

passport888/navidrome-telegram repository overview

navidrome-telegram

What is this

A ready-to-run Navidrome (self-hosted music streaming server, Subsonic/OpenSubsonic compatible, works with most mobile/desktop client apps) image, except the actual music files don't take up any of your own disk or cloud storage — they live on Telegram. Telegram doesn't cap how much a personal account can store, so this is effectively free, unlimited storage for your music library, with no drive or cloud bill to pay.

Problem it solves

Running your own Navidrome normally means either using local disk space or paying for a separate persistent volume/object storage. If the host itself is a free, disposable instance that gets torn down and recreated on its own schedule, there's nowhere for music files to live at all. This project moves the actual storage layer to Telegram (via TGFS, which wraps a Telegram channel as a standard WebDAV endpoint), so the server/container can be rebuilt, destroyed, or moved anywhere — as long as the Telegram channel and its index still exist, nothing is lost, and a fresh container gets you right back to a working library.

Features

  • One docker run brings up the whole stack: Navidrome + Telegram-backed storage + HTTPS reverse proxy — no separate deploys, no manual wiring
  • Pure environment-variable configuration — no files to mount, so it deploys straight onto PaaS hosts like Railway/Northflank/Render where env vars are the only configuration surface, no image rebuild needed to change settings
  • Admin account is created automatically on first run (with the username/password you choose) and reused on every subsequent start/rebuild — playlists, play history and favorites all persist
  • TLS is optional: set TLS_CERT_B64/TLS_KEY_B64 for HTTPS, or leave them out for plain HTTP and put your own reverse proxy/tunnel in front
  • Nothing personal is baked into the image (no keys, passwords, or domains) — safe to share publicly; anyone running it with their own Telegram account and GitHub repo gets a fully independent instance

How it's put together

Everything ships as one Docker container running four processes:

TGFS (wraps a Telegram channel as local WebDAV)
  → rclone mount (mounts it as a local filesystem)
    → Navidrome (reads that mount, serves playback / Subsonic API)
      → nginx (reverse proxy + optional TLS)

All four share the same process group and filesystem namespace inside one container — the key advantage over running them as separate containers: to make Navidrome see new files that showed up on Telegram, you just restart this one container. There's no risk of "the mount lives in one container, the reader lives in another, and restarting them out of order tears the mount" — a real failure mode this design avoids entirely.

Quick start

Everything is configured through environment variables — no config files to mount, so it deploys straight onto PaaS hosts like Railway/Northflank/Render where the only configuration surface is a web form of env vars.

For local testing, --env-file is cleaner than a wall of -e flags:

cp .env.example .env   # fill in real values; .env is already gitignored

docker run -d --name navidrome-telegram \
  --restart unless-stopped \
  --device /dev/fuse --cap-add SYS_ADMIN --security-opt apparmor:unconfined \
  -p 80:80 -p 443:443 \
  --env-file .env \
  -v navidrome-data:/data \
  passport888/navidrome-telegram:latest

Or pass everything as individual -e flags (this is essentially what a PaaS's web form does for you):

docker run -d --name navidrome-telegram \
  --restart unless-stopped \
  --device /dev/fuse --cap-add SYS_ADMIN --security-opt apparmor:unconfined \
  -p 80:80 -p 443:443 \
  -e TG_API_ID=123456 \
  -e TG_API_HASH=xxxx \
  -e TG_BOT_TOKEN=xxxx \
  -e TG_CHANNEL_ID=xxxx \
  -e META_GITHUB_REPO=owner/repo \
  -e META_GITHUB_TOKEN=xxxx \
  -e WEBDAV_USER=someuser \
  -e WEBDAV_PASSWORD=xxxx \
  -e WEBDAV_JWT_SECRET=xxxx \
  -e ND_USERNAME=someuser \
  -e ND_PASSWORD=xxxx \
  -e SRV_DOMAIN=music.example.com \
  -v navidrome-data:/data \
  passport888/navidrome-telegram:latest
  • -v navidrome-data:/data persists Navidrome's account/playlists/play history/scan results. It's a plain Docker volume, so it survives as long as the host's disk does — restarting the container never loses data.
  • /dev/fuse + SYS_ADMIN are required for rclone to do a FUSE mount inside the container. If your hosting platform doesn't allow these (many PaaS providers don't permit custom privileges), this image won't run there — you'll need an environment that lets you set them (your own VPS/server).
Using docker-compose

The repo ships a docker-compose.yml equivalent to the docker run command above:

cp .env.example .env   # fill in real values
docker compose up -d

Environment variables

VariableRequiredDescription
TG_API_ID / TG_API_HASHFrom my.telegram.org
TG_BOT_TOKENA bot created via @BotFather
TG_CHANNEL_IDA private channel's chat_id, with the leading "-100" stripped off. The bot must be added there as admin with "Post Messages" permission enabled
META_GITHUB_REPOA dedicated repo (owner/repo) for TGFS's own file index — keep it separate from other projects, since it generates a large number of automatic commits
META_GITHUB_TOKENA Personal Access Token with write access to that repo
META_GITHUB_BRANCHNo, defaults to main
WEBDAV_USER / WEBDAV_PASSWORDLogin for TGFS's internal WebDAV (used inside the container only, never exposed externally)
WEBDAV_JWT_SECRETAny random string
ND_USERNAME / ND_PASSWORDNavidrome login (auto-created on first container start, read from /data afterward — never recreated)
SRV_DOMAINNo, defaults to _ (catch-all)nginx's server_name; can be anything if you're not using TLS
TLS_CERT_B64 / TLS_KEY_B64NoBase64-encoded cert/key content (not raw PEM — not every platform's env var input reliably handles multi-line text, so a single-line encoding is safer), e.g. base64 -i cert.pem | tr -d '\n'. Set both to serve HTTPS:443, otherwise it's plain HTTP:80 only

Nothing is hardcoded into the image — no keys, passwords, or domains — so it's safe to publish publicly. All configuration flows in through runtime environment variables.

About first-run account creation

Navidrome has no built-in way to specify a custom admin username via configuration — it can only bootstrap one fixed account first, then swap it for the real one. This image's entrypoint handles that automatically: it checks whether /data already has a navidrome.db — if not, it bootstraps a temporary admin, creates the real account, then deletes the temporary one; if the DB already exists, it just starts up against the existing account without recreating anything.

Known limitations

  • TGFS loads its entire file index from GitHub into memory on startup, and this takes longer as the library grows — the bigger the library, the slower every (re)start.
  • TGFS currently has a known bug with HTTP Range (seek) requests — a non-zero byte offset triggers a Telegram API error and crashes/restarts TGFS. rclone's --vfs-cache-mode full lazy mount sidesteps this (a track is fully downloaded to local cache before playback starts, and every subsequent seek reads from that cache) — this is why that cache mode is fixed and not meant to be changed to a streaming/minimal-cache mode.
  • Don't cold-start two TGFS processes on the same TG_BOT_TOKEN at the same time (e.g. this container serving traffic while the same bot is also used elsewhere to upload files) — in testing this caused a connect/disconnect loop that never stabilized. If you need both running concurrently, create a separate bot (@BotFather) and use a different token for each.

Tag summary

Content type

Image

Digest

sha256:614dd0257

Size

293.2 MB

Last updated

8 days ago

docker pull passport888/navidrome-telegram