Monitor your Plex Media Server with comprehensive real-time metrics including:
- Advanced Transcode Analytics - Detailed transcode type detection (video/audio/both) and subtitle handling
- Network & Bandwidth - Real-time transmission tracking and bandwidth monitoring
- Real-time Session Monitoring - Live WebSocket tracking of playback sessions and state changes
- Server Performance - CPU/memory utilization and system health indicators
- Storage & Library Metrics - Library sizes, media counts, and disk usage across all sections
docker run -d \
--name plex-exporter \
-p 9000:9000 \
-e PLEX_SERVER="http://192.168.1.100:32400" \
-e PLEX_TOKEN="your-plex-token-here" \
ghcr.io/timothystewart6/prometheus-plex-exporter
version: '3.8'
services:
plex-exporter:
image: ghcr.io/timothystewart6/prometheus-plex-exporter
container_name: plex-exporter
ports:
- "9000:9000"
environment:
PLEX_SERVER: "http://192.168.1.100:32400"
PLEX_TOKEN: "your-plex-token-here"
restart: unless-stopped
You can supply environment variables via your container runtime, docker run -e VAR=...
, docker-compose
, or with a .env
file. The exporter recognizes the following environment variables:
Variable | Description | Default |
---|---|---|
PLEX_SERVER |
Full URL to your Plex server (required) | (required) |
PLEX_TOKEN |
Plex authentication token used to authenticate with your Plex server (required) | (required) |
LIBRARY_REFRESH_INTERVAL |
How often to re-query expensive per-library counts (music tracks, episode counts, and library items) in minutes. Must be an integer number of minutes. Defaults to 15 when unset. Set to 0 to disable caching and always re-query. |
15 |
LOG_LEVEL |
Log verbosity. One of debug , info , warn , error . Defaults to info . Set to debug for verbose troubleshooting. |
info |
LOG_FORMAT |
Log output format. Set to console for human-readable logs (development), or omit for structured JSON logs (production/containers). |
json |
ENVIRONMENT |
Environment mode. Set to development for console logging, or omit for production JSON logging. |
production |
SKIP_TLS_VERIFICATION |
Optional convenience for connecting to Plex servers with self-signed or mismatched TLS certificates. When set to true the exporter (and the vendored Plex client) will skip TLS certificate verification for both HTTP and websocket connections. THIS IS INSECURE — only use in trusted networks or testing. Defaults to off. |
false |
TZ |
Optional timezone name (IANA location, e.g. America/Chicago ) to use for log timestamps and any local time calculations. If unset the system local timezone is used. |
(system) |
PLEX_CLIENT_TIMEOUT_SECONDS |
HTTP client timeout when contacting the Plex server (seconds). Defaults to 10 . Set higher for slow LANs or when your Plex server takes longer to respond (mostly due to size). |
10 |
Example .env
file:
PLEX_SERVER=http://192.168.1.100:32400
PLEX_TOKEN=abcd1234efgh5678
LOG_LEVEL=info # debug|info|warn|error
TZ=America/Chicago
The exporter defaults to structured JSON logging (ideal for containers and log aggregation systems like ELK, Grafana Loki, or cloud logging). For local development, you can switch to human-readable console logs:
Production/Container Logging (default):
{"level":"info","ts":1724885215.123,"msg":"starting exporter","PLEX_SERVER":"https://plex.local:32400"}
Development/Console Logging:
2025-08-28T19:20:15.123-0500 INFO starting exporter PLEX_SERVER=https://plex.local:32400
To enable console logging, set either:
LOG_FORMAT=console
ENVIRONMENT=development
Startup note:
- On startup the exporter performs a fast, lightweight metadata refresh to discover server and library sections and reduce startup latency and memory use. The expensive per-library full refresh (which computes episode counts, exact music track counts, and full library item lists) is deferred and runs in the background after a short delay. The startup log will always show the effective
LibraryRefreshInterval
or that caching is disabled. - The required
PLEX_SERVER
andPLEX_TOKEN
environment variables are required for the exporter to connect to your Plex server. Note: unit tests in this repository perform a synchronous full refresh so counts are populated deterministically during tests; production behavior defers the heavy work.
Library refresh timing (production)
- The exporter defers the expensive per-library full refresh for 15 seconds after startup (plus a small jitter) and performs a fast, lightweight discovery immediately. This reduces peak memory and server load during startup. The 15s startup delay is a fixed behavior in the binary (not exposed as an environment variable).
Recommendation: Keep the default deferred behavior (15s delay) in production to avoid simultaneous heavy queries and lower peak memory usage. If you're running many exporter instances against one Plex server, consider staggering startups.
The exporter provides comprehensive real-time metrics about your Plex server via WebSocket monitoring:
host_cpu_util
- Host CPU utilization percentagehost_mem_util
- Host memory utilization percentageserver_info
- Server information with version, platform details
library_duration_total
- Total duration of library content in millisecondslibrary_storage_total
- Total storage size of library in bytes
plex_library_items
- Number of items in each library section (movies for movie libraries, shows for TV libraries, artists for music libraries, photos for photo libraries). Includes acontent_type
label to clarify what type of items are being counted.plex_media_episodes
- Total number of TV episodes across all librariesplex_media_music
- Total number of music tracks across all librariesplex_media_movies
- Total number of movies across all librariesplex_media_other_videos
- Total number of other videos (home videos) across all librariesplex_media_photos
- Total number of photos across all libraries
estimated_transmit_bytes_total
- Estimated bytes transmitted to clientsplay_seconds_total
- Total playback duration per session in secondsplays_total
- Total play counts with detailed labels (user, device, media, transcode info)transmit_bytes_total
- Actual bytes transmitted to clients
All session metrics include sophisticated transcode detection:
- Stream Analysis: Source vs destination resolution, bitrate, and codec comparison
- Subtitle Actions:
burn
,copy
,none
, ortranscode
for subtitle handling - Transcode Type:
video
,audio
,both
, ornone
based on codec analysis
All metrics include relevant labels for detailed filtering and grouping:
- Library labels:
library_type
,library
,library_id
- Server labels:
server_type
,server
,server_id
- Session labels:
media_type
,title
,child_title
,grandchild_title
,stream_type
,stream_resolution
,stream_file_resolution
,stream_bitrate
,device
,device_type
,user
,session
,transcode_type
,subtitle_action
Import our pre-built Grafana dashboard for instant visualization:
Dashboard ID: Available in examples/dashboards
This exporter provides advanced real-time monitoring capabilities:
- Live Session Tracking: Connects to Plex's WebSocket API for instant session updates
- State Change Detection: Real-time monitoring of play/pause/stop/buffering states
- Session Lifecycle Management: Automatic session pruning and cardinality control
- Concurrent Library Scanning: Parallel processing of library sections with semaphore control
- Exponential Backoff: Smart retry logic for session and metadata lookups
- Efficient Memory Usage: Automatic cleanup of old session metrics to prevent memory bloat
- Smart Transcode Detection: Analyzes codec changes and Plex decisions for accurate classification
- Bandwidth Estimation: Real-time tracking of transmitted data and network usage
- Session Correlation: Maps transcode sessions to playback sessions for detailed analytics
# Clone the repository
git clone https://github.com/timothystewart6/prometheus-plex-exporter.git
cd prometheus-plex-exporter
# Build the binary
go build -o prometheus-plex-exporter ./cmd/prometheus-plex-exporter
# Run locally
./prometheus-plex-exporter
Currently, the following values are hardcoded but can be modified in the source:
- Listen Port: Hardcoded to
:9000
incmd/prometheus-plex-exporter/main.go
- Metrics Path: Hardcoded to
/metrics
incmd/prometheus-plex-exporter/main.go
-- Log Level: Controlled viaLOG_LEVEL
(debug|info|warn|error). Defaults toinfo
.
To customize these values, modify the constants in main.go
and rebuild.
# Run all tests
go test ./...
# Run with coverage
go test -cover ./...
# Run integration tests
go test ./pkg/plex -run TestIntegration
We welcome contributions! For developer setup, pre-commit checks, linter installation, and the full local workflow please see CONTRIBUTING.md
in the repository root.
You can skip the hook for a single commit with git commit --no-verify
, but avoid doing that for PRs unless there is a valid reason.
We recommend installing golangci-lint
for fast, consistent linting. It runs many linters in parallel and respects the project's .golangci.yml
.
macOS (Homebrew):
brew install golangci-lint
Linux (bash):
curl -sSfL https://raw.githubusercontent.com/golangci/golangci-lint/master/install.sh | sh -s -- -b $(go env GOPATH)/bin v1.59.0
# ensure $(go env GOPATH)/bin is in your PATH
Or see the official instructions: https://golangci-lint.run/usage/install/
Format code:
gofmt -s -w $(git ls-files '*.go')
Lint (preferred):
golangci-lint run ./...
Fallback lint (if you don't have golangci-lint):
go vet ./...
Run tests:
go test ./...
- Fork the repo and create a feature branch from
main
. - Make changes and ensure pre-commit checks pass locally.
- Open a PR against
main
with a clear description of the change.
CI will run the linter and tests on your PR. Please address any lint/test failures before merging.
Thanks — contributions are welcome!
This project follows a rolling release model where the main
branch is automatically built and deployed to GitHub Container Registry (GHCR). Every commit to main
produces a new :latest
image, ensuring you always have access to the newest features and bug fixes.
For production environments requiring stability, we recommend pinning to a specific digest rather than using the :latest
tag. This ensures your deployment won't change unexpectedly.
# Get the current digest
docker pull ghcr.io/timothystewart6/prometheus-plex-exporter:latest
docker inspect ghcr.io/timothystewart6/prometheus-plex-exporter:latest | grep -A 1 "RepoDigests"
# Use the digest in your deployment
docker run -d \
--name plex-exporter \
-p 9000:9000 \
-e PLEX_SERVER="http://192.168.1.100:32400" \
-e PLEX_TOKEN="your-plex-token-here" \
ghcr.io/timothystewart6/prometheus-plex-exporter@sha256:abc123...
version: '3.8'
services:
plex-exporter:
image: ghcr.io/timothystewart6/prometheus-plex-exporter@sha256:abc123def456...
container_name: plex-exporter
ports:
- "9000:9000"
environment:
PLEX_SERVER: "http://192.168.1.100:32400"
PLEX_TOKEN: "your-plex-token-here"
restart: unless-stopped
💡 Tip: You can find specific digests in the GHCR packages page or by using
docker inspect
as shown above.
- jsclayton/prometheus-plex-exporter - The original repo this was forked from
- Prometheus - The monitoring solution
- Grafana - The visualization platform
- All our contributors
If this project helped you, please give it a star!