Installing with Docker
Tunetorch comes as a Docker image. The database, cache and FFmpeg are already included, so all you need is Docker, ideally with Docker Compose.
Quick start
Create a file called docker-compose.yml:
services:
tunetorch:
image: tunetorch/tunetorch:latest
container_name: tunetorch
restart: unless-stopped
ports:
- "5000:5000"
volumes:
# Data directory. Please back it up regularly.
- ./tunetorch-daten:/app/data
# Optional: existing music folder to scan (always read-only).
# - /path/to/your/music:/app/musik:ro
# Optional: collection for the one-off initial import.
# - /path/to/your/collection:/app/import:ro
# Suitable for direct access over HTTP, as described in this quick
# start. Behind a TLS reverse proxy, remove both lines again
# (see below).
environment:
TT_HTTPS: "False" # no TLS: cookies without the Secure flag
TT_TRUSTED_PROXIES: "0" # no proxy in front
# TT_PUID: "1000" # UID of the volume owner
# TT_PGID: "1000" # GID of the volume owner
Start it:
docker compose up -d
You'll then find the web interface at http://<server-address>:5000. The first time, sign in
with admin / BITTE_ÄNDERE_MICH. Tunetorch asks for a new password straight away (see
Getting started).
The volumes in detail
| Path in the container | Required | Purpose |
|---|---|---|
/app/data | yes | Database, uploaded audio files, cover art, images, logs, sign-in keys, cache. This is the directory you need to back up. |
/app/musik | no | An existing music folder that the library scan reads in. Always mount it with :ro. Tunetorch never writes to your music library. |
/app/import | no | An existing collection for the one-off initial import. Also :ro. You can remove the line again after the import. |
You can mount /app/data as a bind mount (./tunetorch-daten:/app/data) or as a
named volume (tunetorch_daten:/app/data). With a bind mount, set TT_PUID/TT_PGID
to your host user (id -u / id -g) so that you can write to it from the host as well.
On start-up, the container hands
/app/dataover to the configured UID:GID. A folder created byroottherefore becomes writable by itself. If that doesn't work (because of ACLs on a Synology, for example), the start aborts with an[entrypoint] FEHLER:message (German for “error”).
Docker only, without Compose
docker run -d \
--name tunetorch \
--restart unless-stopped \
-p 5000:5000 \
-v $(pwd)/tunetorch-daten:/app/data \
tunetorch/tunetorch:latest
Loading a supplied image
If you received the image as an archive:
docker load -i tunetorch-<VERSION>.tar.gz
In the archive the image is called tunetorch:<VERSION>-<arch>, but the Compose file above
expects tunetorch/tunetorch:latest. So you have to rename it. Otherwise the start fails
with “image not found”, and nothing tells you why:
docker tag tunetorch:<VERSION>-amd64 tunetorch/tunetorch:latest
Updating
docker compose pull
docker compose up -d
Your data in /app/data is kept. Tunetorch applies database changes itself on start-up, so
there's nothing to do by hand after an update.
Before larger updates, a backup of the data directory is worth it. See Security & backup.
Running behind a TLS reverse proxy
The quick start above is set up for direct access over HTTP. That's what the two lines
TT_HTTPS: "False" and TT_TRUSTED_PROXIES: "0" are for. If Tunetorch runs behind a reverse
proxy with TLS (Caddy, Traefik, Nginx Proxy Manager), remove both again. The defaults
True and 1 are meant for exactly this case. The proxy has to pass on
X-Forwarded-Proto: https; that's how Tunetorch recognises the encrypted connection.
The reason lies in how browsers handle cookies: with TT_HTTPS: "True", the sign-in cookies
get the Secure flag. If you then open Tunetorch over http://, the browser silently
discards them. The sign-in seems to fail, even though the server accepted it. The server log
then contains a warning that describes exactly this.
The trap during initial setup: over
http://localhost:5000, Chrome, Edge and Firefox acceptSecurecookies anyway, because they treat localhost as secure. If you set up Tunetorch directly on the server, you therefore won't notice a wrong setting. Only access from a second device via the LAN address fails.
If you have the choice, use TLS. TT_HTTPS: "False" is the solution for a home network
without a certificate, not the better way to run it.
More on this under Configuration.
Synology DSM
On Synology systems, a shared folder often doesn't belong to 1000:1000 but to, say,
1026:100. Check the values on the host with ls -ln in the volume directory and enter
them:
environment:
TT_PUID: "1026"
TT_PGID: "100"
If an ACL overrides the automatic change of owner, also explicitly give the user with this UID write permission in File Station.
Two more DSM quirks often cost time on the first start: port 5000 belongs to the DSM
interface (choose a different host port), and a shared folder that you mount as
/app/import or /app/musik is not automatically readable for the container. That you
can open it yourself in File Station means nothing here. Both, including a command to check,
are on the Synology NAS (DSM) page.
Storage for DLNA casting (optional)
If you use DLNA casting with loudness matching or a target format, the server creates short-lived copies of the current track. It decides where by itself: preferably in memory, otherwise on disk. So there's nothing to set up for this.
If your server has plenty of RAM, you can deliberately put the copies in memory and spare the SD card or SSD some writes:
shm_size: "1g"
This is an upper limit, not a reservation. As long as the space isn't needed, it costs nothing. On a Raspberry Pi, though, RAM is scarcer than write cycles, so the default is usually the better choice there.
Passing through a CD drive (optional)
For ripping CDs, the container needs access to a CD drive on the server, built in or connected via USB. The programs it needs are already in the image; only these lines are missing:
volumes:
- /dev:/dev
device_cgroup_rules:
- "b 11:* rmw"
group_add:
- "24"
/dev:/devrather than just/dev/sr0: a USB drive gets a new device entry every time you plug it in. With a single device passed through, the container wouldn't even start while the drive is missing.device_cgroup_rulesunlocks only CD drives (/dev/sr*) from that. Hard disks, USB sticks and everything else stay off limits for the container. You don't needprivileged.group_addis the group that owns the drive on the host. On Debian, Ubuntu and Raspberry Pi OS that's24(cdrom). You can check withstat -c '%g %G' /dev/sr0.
Then switch CD ripping on in the server settings. If the interface reports “No access to the
drive”, the number under group_add is almost always wrong.
On a Synology NAS, these lines usually don't help: DSM doesn't ship the driver for CD drives, so Tunetorch doesn't see a drive. How to check this and what you can do instead is described under Synology NAS (DSM).
Do you also use DLNA casting? With /dev:/dev, the container sees the host's memory
folder, and shm_size (see above) no longer has any effect. The casting copies then end up
in RAM. On a Raspberry Pi, this line under environment: keeps them on disk:
TT_DLNA_TMPFS: "/app/data/temp/dlna"
Raspberry Pi: mind the power supply. When the CD spins up, a USB drive briefly needs more power than the Pi's USB ports deliver. That doesn't show up as a clear error message, but as read errors or as a drive that disappears in the middle of a rip. The fix is a drive with its own power supply or a powered USB hub, meaning one with its own power supply. A Y-cable across two of the Pi's ports doesn't help. A passive splitter with a phone charger plugged into its second port isn't a solution either: Its power lines are connected, so the charger also feeds power back into the Pi.
If your Pi boots from a USB SSD, this matters twice over. The SSD and the drive then share the power of the USB ports. If the voltage drops while the CD spins up, the Pi can briefly lose its SSD and hang: It still answers pings, but no longer responds to SSH or the interface. In that case, connect only the drive to the hub and keep the SSD directly on the Pi. Connected through the hub, the SSD would have one more point where your boot drive can drop out.
To check whether the power supply is sufficient, run vcgencmd get_throttled on the Pi after a
rip. throttled=0x0 means the voltage hasn't dropped once since start-up.
Health check
The image includes a HEALTHCHECK that checks every 30 seconds whether the server responds.
docker ps therefore shows the container as healthy or unhealthy. That way you also
spot a hung server that restart: unless-stopped alone wouldn't notice.
Depending on the hardware, start-up takes up to a minute (on one NAS it was 44 seconds),
because Redis, the database check and the server start one after another. During that time
the container doesn't count as healthy yet, and a “Bad Gateway” from the reverse proxy is
normal. It goes away by itself. If it stays, the cause lies elsewhere; see
Troubleshooting.
docker ps
docker compose logs -f tunetorch
Next step
- Configuration: environment variables and server settings
- Adding music: bringing your music in