Security & backup
Self-hosting means your data lives with you, and so does the responsibility for it. Tunetorch makes that as easy as possible for you. There's exactly one directory you need to back up, and the server sends nothing to the outside on its own.
Privacy
Tunetorch collects no telemetry. The server sends neither usage statistics nor crash reports to us or to third parties. There's no tracking and no advertising, not in the Android app either. Music, playlists, listening history, statistics and accounts live in the database on your server, and only there.
The server only connects to the outside for features you switch on yourself. There are exactly these four, and the table names every service they contact:
| Connection | When | To whom | What is sent | Requirement |
|---|---|---|---|---|
| Online image search | When you search in the image dialog | MusicBrainz, Cover Art Archive (Internet Archive), Wikidata, Wikimedia Commons | The search term, i.e. the name of the artist or album | Pro |
| MusicBrainz enrichment | When you look up an artist or a CD, or click “Check on MusicBrainz” in a split-up suggestion | MusicBrainz, plus Wikidata, Wikimedia Commons and Wikipedia for the photo and biography, and when ripping CDs the Cover Art Archive (Internet Archive) for the cover | The name of the artist or album, or the CD's ID; when checking a split-up suggestion, the artist together with the track or album name. The Cover Art Archive only receives the MusicBrainz ID of the chosen release | Pro |
| Licence check | When you enter the Pro code and once a day after that | tunetorch.de | Only your installation's ID and a key that binds the licence to your server | Pro |
| AccurateRip | Once per CD when ripping CDs | AccurateRip | Only the CD's ID, calculated from its table of contents | CD ripping switched on |
Your music, your library and your usage data go along with none of them.
CD ripping is off out of the box. When you switch it on, the interface explicitly asks first, and the AccurateRip check can be switched off separately. It's included without Pro too, because it safeguards the quality of the rip. So a Free installation without CD ripping doesn't open a single connection to the internet.
In the browser, too, everything stays with you: the interface loads nothing from third-party servers. Images from the online image search are fetched by the server, not by your browser.
Within your server:
- Personal statistics belong to the account in question. Other users don't see them.
- Private notes can only be seen by whoever wrote them, not even by the admin.
- The counter for the most played tracks only stores how often a track played, without any link to an account. That makes it anonymous.
What needs backing up
/app/data. Nothing more.
/app/data/
├── db/ the database: tracks, albums, artists, accounts, playlists, statistics
├── backups/ backup copies of the database (see below)
├── audio/ uploaded original files + metadata backup
├── cover/ cover art in four sizes
├── interpret_hero/ artist images
├── person_hero/ photos of people
├── jwt/ signing keys for sign-in
├── lizenz/ state of the Pro licence
├── redis/ persistent blocks
└── logs/ server logs
You don't need to back up audio_cache/ (converted versions that can be recreated at any
time), temp/ and upload_temp/.
If your collection lives in a mounted folder (/app/musik), back that up separately as usual.
Tunetorch never writes to it.
Making a backup
With a bind mount, the simplest way is this:
docker compose stop tunetorch
tar czf tunetorch-backup-$(date +%F).tar.gz ./tunetorch-daten
docker compose start tunetorch
If you back up while it's running, with Borg, restic or rsync for example, you should know: the database works in WAL mode and is robust because of it. A snapshot taken in the middle of a write can still be incomplete. A backup is safest at night, after the maintenance tasks.
The nightly “Compact the database” task helps the backup. It frees unused storage and keeps the difference between two backups small.
Restoring
Put the directory back and start the container. Tunetorch recognises the state of the database and applies any necessary changes itself.
The sign-in key belongs in the backup. If
jwt/is missing, Tunetorch creates a new one on start-up, and all existing sign-ins become invalid. No data is lost, but everyone has to sign in again.
Tunetorch backs up by itself before every schema update
If an update changes the structure of the database, Tunetorch stores a copy of the database
beforehand under /app/data/backups/. That doesn't happen on a normal restart, only when
something really changes.
docker exec -it tunetorch tt db-sicherungen
Name Art Schema Größe
auto-schema23-20260819-1930.db auto 23 11.4 MB
manuell-schema22-20260810-1200.db manuell 22 11.2 MB
(The tool's output is in German: Art is the kind of copy, Größe the size.)
auto copies are created before a change and show the state before it. The two newest are
kept. manuell (manual) copies you create yourself with tt db-sichern, and Tunetorch
never deletes them.
Restoring if something isn't right after an update:
docker exec -u root -it tunetorch tt db-einspielen auto-schema23-20260819-1930.db
The command first backs up the current state, stops the server, restores the copy and starts
the server again. After that you still need the old image. Otherwise the new server
applies the same change again straight away on start-up. So enter the previous version in the
docker-compose.yml instead of latest.
Older versions are available on Docker Hub for this: a superseded version stays available
there for at least six months, and one version for each database structure stays there for
good. If you want to keep a particular version for longer, save it yourself:
docker save tunetorch/tunetorch:0.1.0 | gzip > tunetorch-0.1.0.tar.gz, and back with
docker load < tunetorch-0.1.0.tar.gz.
Why
-u root: only that way is the command allowed to stop the running server. Without it, it aborts and tells you why.
These copies are not three things:
- No substitute for your backup. They live in the same directory as the database. If the drive breaks, they're gone with it. They also only contain the database, no music and no cover art.
- Not automatically up to date. No new copy is created between two updates. If you want a
state from lunchtime today, create it yourself with
tt db-sichernor in the server settings (see below). - No automatic rollback. If a change goes wrong, the server stops and touches nothing. What gets restored is your decision.
Backup from the interface
You can also create a manuell copy without the command line: Server settings → System &
maintenance → Database backup → Create backup now. Below it, all existing copies are
listed with time, type and size. That's worthwhile before larger changes, for example before
you press Apply all in the metadata assistant; its confirmation offers
you the same button. Only admins have the right to do this.
Restoring and downloading are deliberately only possible on the server. To restore,
Tunetorch pauses briefly, which the interface can't do by itself. And the copy contains the
whole database including sign-in data, so it doesn't belong behind a download link. The list
shows the matching tt db-einspielen command right away.
Metadata backup
Next to each uploaded original file, Tunetorch stores a small text file with its metadata. Tunetorch never reads it during operation. It's only a safety net in case the database is lost but the music files are still there.
Sign-in
Tunetorch stores the sign-in in protected cookies (HttpOnly), not in the browser's storage. A script on the page therefore can't get at your sign-in data.
| Cookie | Default lifetime | Purpose |
|---|---|---|
| Access | 15 minutes | All normal requests, including music and cover art. |
| Refresh | 7 days | Renews the access token in the background. |
The refresh lifetime counts from your last use, not from signing in: open Tunetorch again within that time and you get a fresh sign-in and stay signed in. You are only signed out after that long without using it at all.
Both lifetimes can be changed in the server settings (access at most 1 day, refresh at most 90 days). The upper limits prevent an accidentally high value from keeping a stolen sign-in valid for as long as it likes.
When you sign out, the server blocks both tokens. They're worthless immediately, even if someone had intercepted them. The blocklist lives on disk and survives a restart. In the Android app this happens when you remove a server connection: it first signs you out on the server and then deletes the data on the device. Otherwise the tokens would stay valid until they expire, even though nothing of them can be seen on the device any more.
With the Stay signed in switch you decide when signing in whether you stay signed in after closing the browser.
Brute-force protection
Failed sign-in attempts are counted per IP address. If there are too many within the set period, Tunetorch blocks the address for a certain time.
| Setting | Default |
|---|---|
| Time window | 10 minutes |
| Failed attempts until blocked | 10 |
| Block duration | 60 minutes |
In addition, the response to a wrong username takes just as long as to a wrong password. So the response time doesn't reveal whether an account exists.
For this,
TT_TRUSTED_PROXIEShas to be right. Behind a reverse proxy the server would otherwise only see the proxy's IP and lock everyone out with a block. If the value is too high, on the other hand, the caller's IP can be faked. See Configuration.
Hardening in the browser
- Strict Content Security Policy: the interface loads nothing from third-party servers. Tunetorch brings its own fonts and charts.
X-Frame-Options: DENY: the interface can't be embedded in other sites (protection against clickjacking).Referrer-Policy: no-referrer,X-Content-Type-Options: nosniffand HSTS when HTTPS is active.- CORS is off out of the box, because the interface and the API come from the same address.
- No entry in search engines: the server delivers a
robots.txtthat blocks everything and marks every response withX-Robots-Tag: noindex, nofollow. So your library doesn't show up in search results, not even individual cover art or tracks. This applies automatically and can't be switched off by accident.
Previews from the online image search go through the server, not directly through the browser. That keeps the strict policy in place, and your browser makes no contact with third-party servers.
Access from the internet
If you want to listen on the go, you need a reverse proxy with a valid TLS certificate. Without HTTPS
- the browser discards the sign-in cookies (the sign-in seems to fail),
- the PWA can't be installed,
- your music and your password travel across the network unencrypted.
An example for Caddy and Nginx is under Configuration.
Instead of opening a port, you can also use a VPN into your home network (WireGuard, Tailscale) or a tunnel. Then you don't need any port forwarding at all.