import Navbar from '@/components/Navbar'; import Footer from '@/components/Footer'; import DocsLayout from '@/components/docs/DocsLayout'; export const metadata = { title: 'Install Plex — DashCaddy Docs', description: 'Install and configure Plex via DashCaddy. Stream your personal media collection anywhere', }; export default function plexDocsPage() { return (
plexinc/pms-docker:latest
Stream your personal media collection anywhere
Plex ships as a self-contained Docker image that DashCaddy provisions with one click. DashCaddy handles the container lifecycle, DNS record, Caddy reverse-proxy entry, and HTTPS certificate so you can focus on using Plex, not installing it.
https://status.sami; configurable via the dashboardHost setting in config.json).POST /api/v1/auth/keys to create one)./media. The deploy form / API payload config.mediaPath must be readable by the container UID (usually 1000).https://status.sami (or your host's dashboard URL).plex), host port (default: 32400), and the media library path, and the claim token./web/index.html) to pass.{success, containerId, url, message, setupInstructions} — there is no separate status-poll endpoint; the dashboard updates live.Authenticate with an API key (or a JWT minted via POST /api/v1/auth/jwt). Send as X-API-Key: dk_... or Authorization: Bearer <jwt>.
curl -X POST https://status.sami/api/v1/apps/deploy \\
-H "X-API-Key: dk_your_api_key" \\
-H "Content-Type: application/json" \\
-d '{
"appId": "plex",
"config": {
"subdomain": "plex",
"port": 32400,
"mediaPath": "/media",
"plexClaimToken": "<get fresh token from https://plex.tv/claim>"
}
}'
The full body schema is in src/utilities/validate.js (Joi schema appDeploy). All config.* fields except subdomain are optional. Notable options:
config.port — host port (1–65535). Defaults to the template's defaultPort.config.mediaPath — host directory to mount as the media library.config.plexClaimToken — Plex claim token (when the upstream service needs one).config.useExisting: true + existingContainerId — attach DashCaddy metadata to an already-running container instead of pulling a new image.config.tailscaleOnly: true — restrict the reverse-proxy entry to your Tailscale network.config.allowedIPs — array of CIDR ranges allowed past the reverse proxy.config.createDns: false — skip DNS record creation (use when the subdomain already resolves).config.resources — {memory, cpus} limits applied to the container.The Intent Router returns a structured intent, not a deployed container. To deploy via AI, send natural language to:
curl -X POST https://status.sami/api/v1/ai/intent \\
-H "X-API-Key: dk_your_api_key" \\
-H "Content-Type: application/json" \\
-d '{ "message": "Deploy Plex on my home host and expose it at plex.sami" }'
The response includes intent, action, parameters, and followup — your client (or the MCP server) must then call POST /api/v1/apps/deploy with those parameters to actually provision the container.
For AI agents (Claude Desktop, Hermes, etc.) configure the MCP server (src/mcp/mcp-server.js) with:
DASHCADDY_URL=https://status.sami:3001 # internal API URL, may differ from dashboard URL
DASHCADDY_API_KEY=dk_your_api_key
The server exposes dashcaddy_deploy_app. Note: this tool writes the Caddy route and creates the services.json entry, but it does NOT pull the Docker image or start the container. You must run docker pull plexinc/pms-docker:latest and start the container yourself for the URL to actually serve traffic. For a fully-managed deployment, call POST /api/v1/apps/deploy directly from your agent.
The media mount path you pass as mediaPath in the deploy payload is mounted as /data inside the container. Bind a host directory containing your media library (movies, TV shows, music, etc.).
chown -R 1000:1000 /media on the host.Get from https://plex.tv/claim - expires in 4 minutes!
Pass it as plexClaimToken inside the config object of the deploy payload (NOT as an environment variable).
Heads up: Plex Claim Token expires within minutes. Get a fresh one from https://plex.tv/claim right before you click Deploy.
DashCaddy creates these volume mounts in the container spec:
/opt/plex/config:/config/opt/plex/transcode:/transcodeMEDIA_PATH:/dataAll paths are host paths (left side) mapped into the container (right side). Restarting the container preserves data; reinstalling the template preserves it unless you explicitly pass config.useExisting: false AND wipe the volume. Bind mounts use the host-path conventions above (e.g. /opt/plex/config becomes a bind mount to the host directory of the same path).
PLEX_CLAIMADVERTISE_IPPLEX_UIDPLEX_GIDThese are baked into the template at deploy time and shipped to the container. The deploy handler does NOT allow overriding them via the API payload — to change them you must edit the template definition in dashcaddy-api/src/docker/app-templates.js.
There is no built-in auto-update — DashCaddy does NOT poll for new image digests. To pull a new version:
docker pull plexinc/pms-docker:latest.docker restart <containerId> (find the ID via GET /api/v1/services or the dashboard).0 0 4 * * * = 04:00 daily).The default backup policy includes the entire /app/data/ directory (services, config, credentials, stats) AND all Docker volumes. Backups are scheduled via backup-config.json — the default schedule is configurable, not "nightly" out of the box. To restore on a fresh host, redeploy the same template and then call POST /api/v1/apps/{appId}/restore with a backup ID from GET /api/v1/backups/history.
Common issues with Plex:
mediaPath is readable by the container UID and that the directory contains the file extensions Plex indexes.GET /api/v1/dns/records and systemctl status caddy on the host./web/index.html is not returning 200. Inspect docker logs <containerId> directly.For layer-by-layer diagnostics, see the Troubleshooting guide.
Template ID: plex. Source: dashcaddy-api/src/docker/app-templates.js.