docs: how to run Lyra behind a Gluetun VPN container
Adds a "Running behind a VPN (Gluetun)" README section: the whole-stack network_mode: service:gluetun setup (DATABASE_URL @db -> @localhost, ports moved to gluetun, FIREWALL_INPUT_PORTS + FIREWALL_OUTBOUND_SUBNETS), a compose sketch, and caveats (Qobuz geo-sensitivity, the IPv6 sysctl not applying but harmless, MB per-IP rate limit covered by the cache, bind mounts unaffected). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -156,3 +156,89 @@ Lyra has **no per-user accounts**. Two layers protect it:
|
|||||||
cookie, plain HTTP on the LAN). For anything internet-facing, also put Lyra behind a
|
cookie, plain HTTP on the LAN). For anything internet-facing, also put Lyra behind a
|
||||||
reverse proxy with TLS and/or a VPN or auth gateway — don't rely on the shared password
|
reverse proxy with TLS and/or a VPN or auth gateway — don't rely on the shared password
|
||||||
alone.
|
alone.
|
||||||
|
|
||||||
|
## Running behind a VPN (Gluetun)
|
||||||
|
|
||||||
|
Lyra runs fine with its traffic routed through a [Gluetun](https://github.com/qdm12/gluetun)
|
||||||
|
VPN container — useful for hiding the download traffic (Soulseek / Qobuz / YouTube). Nothing
|
||||||
|
in Lyra is VPN-incompatible; the only thing that changes is container networking, because a
|
||||||
|
service using `network_mode: "service:gluetun"` gives up its own network stack and shares
|
||||||
|
Gluetun's. Two consequences:
|
||||||
|
|
||||||
|
- Containers that share Gluetun's namespace reach each other over **`localhost`**, not by
|
||||||
|
Docker service name — so `DATABASE_URL` changes from `@db:5432` to **`@localhost:5432`**.
|
||||||
|
- **Published ports move to the `gluetun` container** (the individual services can no longer
|
||||||
|
publish their own).
|
||||||
|
|
||||||
|
The simplest, most consistent setup is to put the **whole stack** behind Gluetun. Sketch
|
||||||
|
(merge into your existing Gluetun compose; adjust the VPN provider block to yours):
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
services:
|
||||||
|
gluetun:
|
||||||
|
image: qmcgaw/gluetun
|
||||||
|
cap_add: [NET_ADMIN]
|
||||||
|
environment:
|
||||||
|
# ... your VPN provider / credentials ...
|
||||||
|
FIREWALL_INPUT_PORTS: "3000" # let your browser reach the web UI
|
||||||
|
FIREWALL_OUTBOUND_SUBNETS: "192.168.0.0/16" # your LAN — so the worker can reach slskd
|
||||||
|
ports:
|
||||||
|
- "8770:3000" # Lyra web (published on gluetun, not on `web`)
|
||||||
|
# - "5432:5432" # only if you want DB access from the LAN
|
||||||
|
|
||||||
|
web:
|
||||||
|
build: ./web
|
||||||
|
network_mode: "service:gluetun" # no `ports:` / `networks:` here
|
||||||
|
environment:
|
||||||
|
DATABASE_URL: postgresql://lyra:lyra@localhost:5432/lyra # `db` -> `localhost`
|
||||||
|
LYRA_SECRET_KEY: ${LYRA_SECRET_KEY}
|
||||||
|
LYRA_PASSWORD: ${LYRA_PASSWORD:-}
|
||||||
|
HOSTNAME: 0.0.0.0
|
||||||
|
LYRA_LIBRARY_ROOT: /music
|
||||||
|
volumes:
|
||||||
|
- ${MUSIC_DIR:-./music}:/music
|
||||||
|
depends_on:
|
||||||
|
gluetun: { condition: service_healthy }
|
||||||
|
db: { condition: service_healthy }
|
||||||
|
|
||||||
|
worker:
|
||||||
|
build: ./worker
|
||||||
|
network_mode: "service:gluetun"
|
||||||
|
environment:
|
||||||
|
DATABASE_URL: postgresql://lyra:lyra@localhost:5432/lyra
|
||||||
|
LYRA_SECRET_KEY: ${LYRA_SECRET_KEY}
|
||||||
|
STAGING_ROOT: /staging
|
||||||
|
SLSKD_DOWNLOADS_ROOT: /slskd-downloads
|
||||||
|
volumes:
|
||||||
|
- ${MUSIC_DIR:-./music}:/music
|
||||||
|
- ${STAGING_DIR:-${MUSIC_DIR:-./music}/.staging}:/staging
|
||||||
|
- ${SLSKD_DOWNLOADS_DIR:-./slskd-downloads}:/slskd-downloads
|
||||||
|
depends_on:
|
||||||
|
gluetun: { condition: service_healthy }
|
||||||
|
db: { condition: service_healthy }
|
||||||
|
|
||||||
|
db:
|
||||||
|
image: postgres:17
|
||||||
|
network_mode: "service:gluetun" # shares the namespace so `localhost:5432` resolves
|
||||||
|
environment:
|
||||||
|
POSTGRES_USER: ${POSTGRES_USER}
|
||||||
|
POSTGRES_PASSWORD: ${POSTGRES_PASSWORD}
|
||||||
|
POSTGRES_DB: ${POSTGRES_DB}
|
||||||
|
volumes:
|
||||||
|
- postgres-data:/var/lib/postgresql/data
|
||||||
|
```
|
||||||
|
|
||||||
|
(Give `db-backup` the same `network_mode` and `PGHOST: localhost`.) The `HOSTNAME=0.0.0.0`
|
||||||
|
and the built-in healthchecks keep working, since everything is in one namespace.
|
||||||
|
|
||||||
|
Caveats worth knowing:
|
||||||
|
|
||||||
|
- **Qobuz is geo-sensitive** — its catalog/availability varies by country, so pick a VPN
|
||||||
|
exit in a region where your Qobuz account works, or some downloads will fail to match.
|
||||||
|
- The worker's `net.ipv6.conf.all.disable_ipv6` sysctl **can't apply behind Gluetun** (a
|
||||||
|
shared netns can't set sysctls) — harmless, because Gluetun blocks IPv6 anyway, which is
|
||||||
|
exactly what that sysctl worked around (Qobuz's CDN resolving to an unroutable IPv6).
|
||||||
|
- MusicBrainz rate-limits per IP; a shared VPN exit is marginally more likely to hit the
|
||||||
|
1 req/s ceiling, but the built-in response cache makes that a non-issue in normal use.
|
||||||
|
- Bind mounts (`/music`, `/staging`, `/slskd-downloads`), the Postgres volume, and backups
|
||||||
|
are filesystem, not network — **completely unaffected** by the VPN.
|
||||||
|
|||||||
Reference in New Issue
Block a user