# Plex **Purpose:** Self-hosted media server for streaming TV and movies from the local library. | Property | Value | | ------------ | ----------------------------------------------------------------------------- | | Image | `lscr.io/linuxserver/plex:latest` | | Web UI | | | Compose file | [`../services/plex/docker-compose.yml`](../services/plex/docker-compose.yml) | Current upstream version: **1.43.4.10903** (`1.43.4.10903-e5521bd8c-ls324`). The `latest` tag is used deliberately — LinuxServer recommends it for Plex, and `VERSION=docker` lets the image track the current public build. Plex also self-updates inside the container. --- ## Environment Variables | Variable | Description | | ------------- | ---------------------------------------- | | `PUID` | Host user ID for volume ownership | | `PGID` | Host group ID for volume ownership | | `TZ` | Timezone (e.g. `Europe/Berlin`) | | `TV_PATH` | Host path to TV library | | `MOVIES_PATH` | Host path to movies library | | `PLEX_CLAIM` | Claim token for first-time setup | `VERSION=docker` is set in the compose file (not Portainer) and means "let the image decide the Plex version". --- ## First-time setup (claiming) Plex refuses the setup wizard when the request arrives through a reverse proxy with an unknown `Host` header (it treats it as non-local), so the web UI shows *"Not authorized — You do not have access to this server"* until the server is claimed. Claim it with a **claim token**: 1. Open while logged in and copy the token (it expires after ~4 minutes). 2. Put it in the `PLEX_CLAIM` value of the `plex` stack in Portainer. 3. Restart the stack: `knecht restart plex` (or `knecht update plex`). 4. Open — the server is now claimed to your account. `PLEX_CLAIM` can be emptied again afterwards; it is only read while the server is unclaimed. ### After claiming Plex clients (Apple TV, mobile, TV apps) do **not** use the Traefik domain. They read the server's connection list from plex.tv and connect directly, so two things are needed: 1. **Publish the client ports** — see [Ports](#ports) below. Otherwise the only advertised address is the container IP (`172.18.0.11`), which no client can reach. 2. **Custom server access URLs** = `http://192.168.178.193:32400` (host LAN IP) and optionally `https://plex.home.jens.pub`. Set via **Settings → Network** or the API: ```sh curl -X PUT "http://192.168.178.193:32400/:/prefs\ ?customConnections=http://192.168.178.193:32400,https://plex.home.jens.pub\ &allowedNetworks=192.168.178.0/24,172.18.0.0/16\ &X-Plex-Token=" ``` The token lives in `/config/Library/Application Support/Plex Media Server/Preferences.xml` (`PlexOnlineToken`). `allowedNetworks` (LAN Networks) makes the LAN and the Docker subnet count as local, avoiding the remote bandwidth cap. After this, plex.tv advertises e.g. `https://192-168-178-193..plex.direct:32400` (`local=1`) and clients connect directly. --- ## Ports Bridge networking means Plex clients can only reach the server if its ports are published on the host. GDM multicast discovery does **not** traverse the Docker bridge — clients rely on the plex.tv connection list instead. | Port | Purpose | | --------------- | ------------------------- | | `32400/tcp` | Web UI + client streaming | | `8324/tcp` | Plex Companion | | `32469/tcp` | DLNA | | `32410-32414/udp` | GDM local discovery | `1900/udp` (SSDP) and `5353/udp` (mDNS) are deliberately **not** published — they clash with the host's own Avahi/SSDP services and aren't needed for connectivity. If clients still misbehave (e.g. rely on GDM discovery), `network_mode: host` is LinuxServer's recommended mode and makes discovery work — at the cost of the Traefik route for the web UI. --- ## Volumes | Volume | Mount | Purpose | | -------------- | ------------------ | ---------------------------- | | `plex_config` | `/config` | Database, metadata, settings | | `${TV_PATH}` | `/media/tv:ro` | TV library (read-only) | | `${MOVIES_PATH}`| `/media/movies:ro`| Movies library (read-only) | | tmpfs | `/transcode` (4 GB)| RAM transcoding scratch | Media is mounted read-only — Plex only reads, Sonarr/Radarr manage the files. The transcode directory is a **tmpfs** rather than a named volume: LinuxServer removed `/transcode` volume support, and transcoding to RAM is faster and avoids SSD wear. The 4 GB is a cap, not a reservation. --- ## Networks - `proxy` (external) — Traefik routes `plex.home.jens.pub` → port `32400` - Host ports `32400`, `8324`, `32469`, `32410-32414/udp` published directly (see [Ports](#ports)) Plex is intentionally **not** routed through the VPN. Like Jellyfin it serves local clients and doesn't need outbound anonymity. ### Remote access Because `32400` is published on the host, Plex's remote-access check passes and it is reachable from outside the LAN (router port-forward / UPnP required). The public address is reported by Plex as the WAN IP.