# Hosting Minstrel Minstrel serves plain HTTP on port 4533 and never terminates TLS itself. On a home network or a VPN you trust, that is all you need. Once the server is reachable from the internet, put it behind a reverse proxy that speaks HTTPS, and tell Minstrel about that proxy. This page covers both. ## On a LAN or VPN Publish the port to the network and use `http://:4533`: ```yaml ports: ['4533:4533'] ``` If nothing sits in front of Minstrel, set **Admin → Integrations → Client IP detection** to 0. It defaults to 1, which assumes one proxy (see below for why that matters). ## On the internet Three things, all required: 1. **Don't publish 4533 to the world.** Bind it to the loopback interface so only the proxy on the same host can reach it: ```yaml ports: ['127.0.0.1:4533:4533'] ``` If the proxy runs in another container on the same Docker network, drop `ports:` entirely and point the proxy at `minstrel:4533`. 2. **Terminate HTTPS at a reverse proxy.** Any proxy works. With Caddy, which gets and renews certificates by itself: ``` music.example.com { reverse_proxy 127.0.0.1:4533 } ``` Two settings matter for every proxy: - **Don't buffer responses.** Live updates use Server-Sent Events and audio is streamed; a buffering proxy holds both back. Caddy streams by default. For nginx, set `proxy_buffering off;`. - **Allow long-lived responses.** An event stream stays open for as long as the app is. Raise the proxy's read timeout well past a minute (nginx: `proxy_read_timeout 1h;`). 3. **Tell Minstrel where it lives**, in **Admin → Integrations**: - **Client IP detection:** the number of proxies in front of Minstrel. One proxy (Caddy, Traefik, nginx) is 1; Cloudflare in front of Traefik is 2. Minstrel uses this to find the real client address, for login rate limits and the sessions list, and to tell whether a request arrived over HTTPS. - **Public address:** the URL people use, e.g. `https://music.example.com`. Password-reset emails link here. **Until it is set, no reset email is sent**, so a forged `Host` header can never put a reset token on a link to someone else's server. ### Why the proxy count matters Minstrel only believes `X-Forwarded-For` and `X-Forwarded-Proto` from the number of proxies you configure, counted from the right. Set it too high, or leave it at the default of 1 with no proxy in front, and a client can write those headers itself: it could claim any address to slip past the per-address login limit, or claim HTTPS. With no proxy, set it to 0. When the proxy reports HTTPS, Minstrel marks the session cookie `Secure` and sends `Strict-Transport-Security`. Over plain HTTP it does neither, and it never redirects to HTTPS, so LAN use keeps working unchanged. ## First account A fresh server with no accounts prints a **setup token** in its log: ``` docker compose logs minstrel | grep setup_token ``` Creating the first account (which becomes the admin) requires that token, so whoever reaches a newly exposed server first can't claim it. The token changes on every restart until an account exists. ## Android app The app connects over whatever URL you give it, HTTP or HTTPS. Once the server has a public HTTPS address, give the app that address so the session cookie never crosses the internet unencrypted. See [security notes](./security.md#android-allows-plain-http) for why plain HTTP is still allowed.