Your private, self-hosted music streaming server with a premium CLI workflow.
No VM. No GUI desktop app. Just your music, your server, your rules.
Created and maintained by @Paidguy
PrivateTunes runs a personal music server (Navidrome) behind Caddy (automatic HTTPS), backed by a ./music folder you control.
For music acquisition, it integrates a powerful backend via SpotiFLAC-CLI and provides a premium interactive CLI (scripts/privatetunes.sh) to download music directly into your library — including batch downloads with progress tracking, smart retries, a download queue, and download history.
- Premium CLI UI — 256-color palette, animated spinners, progress bars, status badges
- Modular architecture — 10 focused modules instead of one monolith
- Auto-update system — Git-based with semver comparison and safe rollback
- Permission auto-fix — Never manually
chmodagain - Smart downloads — Automated API rotation across Tidal/Qobuz/Amazon, error analysis, queue management, and retry workflows
- Debug mode —
--debugflag for verbose troubleshooting
| Service | Role |
|---|---|
| Caddy | Reverse proxy + automatic TLS certificates |
| Navidrome | Music server with Subsonic API + web player |
| Syncthing (optional) | Sync your ./music folder across devices |
| SpotiFLAC-CLI | CLI downloader (runs on the host, saves into ./music) |
- Linux server (Ubuntu/Debian recommended)
- Docker + Docker Compose plugin (
docker compose) - Ports:
80and443exposed (for HTTPS) - A domain pointing to the server (DuckDNS works well — it's free)
git clone https://github.com/Paidguy/PrivateTunes.git
cd PrivateTunes
# Full automated setup:
# → Installs Docker Engine + Docker Compose plugin (if missing)
# → Installs system dependencies (curl, ca-certificates, git, gnupg)
# → Creates required directories (music/, data/navidrome/, data/syncthing/)
# → Downloads SpotiFLAC-CLI into ./bin/
# → Copies .env.example → .env and runs the domain wizard
# → Starts the Docker stack
# → Waits for Navidrome to be healthy
sudo ./scripts/setup.sh
# Then use the interactive menu to download music & manage everything:
./scripts/privatetunes.shNote: Permissions are auto-fixed on every startup — no manual
chmodneeded.
Run:
./scripts/privatetunes.sh # Normal mode
./scripts/privatetunes.sh --debug # Verbose debug output| Key | Action | Description |
|---|---|---|
s |
Setup wizard | Guided 5-step onboarding (domain, tools, Docker, admin) |
u |
Check for updates | Git-based auto-update with version comparison |
h |
Help & docs | Comprehensive help screen |
1 |
Install/Update SpotiFLAC | Download the latest binary |
2 |
Download from URL (Now) | Paste a track/album/playlist URL → FLAC into ./music |
3 |
Queue URL | Add URL to links.txt for batch processing later |
m |
Track metadata | Inspect metadata for any Spotify track URL |
b |
Batch process queue | Download all URLs from links.txt with progress tracking |
d |
Download history | View, clear, or queue failed tracks for retry |
4 |
Start stack | docker compose up -d with health monitoring |
5 |
Stop stack | docker compose down |
6 |
View logs | Follow live container logs |
7 |
Stack status | Health checks + disk usage |
8 |
Restart Navidrome | Restart only the music server |
9 |
Domain wizard | Configure DOMAIN, UID, GID |
c |
Backup config | Archive .env, Caddyfile, docker-compose.yml |
p |
Paths & environment | Display paths, stats, and .env contents |
0 |
Exit | Quit |
Create your .env file:
cp .env.example .env| Variable | Description | Default |
|---|---|---|
DOMAIN |
Your domain name | your-domain.duckdns.org |
UID |
User ID for Syncthing | 1000 |
GID |
Group ID for Syncthing | 1000 |
PrivateTunes tracks every download in a persistent JSON database (data/download_history.json):
- No duplicate downloads — Automatically skips previously downloaded content
- Filesystem-aware — Scans
music/for existing files on first run - Resume interrupted batches — Re-run picks up exactly where it left off
- Smart retries — Exponential backoff (5s → 15s → 45s) with rate-limit detection
- Error analysis — Detects DNS failures, 502 errors, timeouts, and adjusts strategy
- Atomic tracking — Each download is recorded immediately via tmp+mv writes
- URL normalization — Same track won't re-download even with different
?si=...params
Use menu option d to:
- View download statistics and recent downloads
- Clear failed entries (so they get retried on next batch)
- Clear all history (to force re-downloading)
- Scan existing music into the history database
Note:
jqis recommended for the best experience. Withoutjq, a simpler log fallback is used.
PrivateTunes/
├── Caddyfile # Caddy reverse proxy config
├── Caddyfile.example # Template Caddyfile
├── docker-compose.yml # Docker stack definition
├── .env.example # Environment template
├── links.txt # Batch download URLs (one per line)
├── music/ # Your music library (Navidrome reads here)
├── data/ # Persistent state
│ └── download_history.json # Download tracking (auto-created)
├── bin/ # Local tools (git-ignored)
└── scripts/
├── privatetunes.sh # Main CLI entry point
├── setup.sh # Host bootstrap wizard
└── lib/ # Modular components
├── ui.sh # UI: colors, boxes, spinners, progress
├── permissions.sh # Auto-fix file permissions
├── updater.sh # Git-based auto-update system
├── api_resolver.sh # API health tracking & fallback
├── downloader.sh # Download engine with retries
├── history.sh # Persistent download history
├── docker.sh # Docker stack management
├── config.sh # .env management & backup
└── onboarding.sh # Setup wizard & help
PrivateTunes checks for updates automatically on startup. You can also check manually:
./scripts/privatetunes.sh
# → Press [u] to check for updatesTo update containers:
docker compose pull
docker compose up -dRun with verbose output to diagnose issues:
./scripts/privatetunes.sh --debug- Verify files are in
./music/ - Restart Navidrome or trigger a scan in the UI:
docker compose restart navidromedocker compose logs -f caddyMake sure:
DOMAINin.envis correct- Ports
80/443are reachable publicly - Your DNS A/AAAA record points to the server
Permissions are auto-fixed on every startup. If you still have issues:
chmod +x scripts/*.sh scripts/lib/*.sh# View all logs
docker compose logs -f
# Check health status
docker compose psPrivateTunes is created and maintained by @Paidguy.
This project is for personal use with legally obtained content. You are responsible for compliance with local laws and the terms of services you use.
Licensed under the MIT License. See LICENSE.