Plex LDAP Gateway
Some apps only know how to log in against LDAP. Plex does not speak LDAP. This project sits in the middle and makes your Plex users look like LDAP users so those apps can authenticate with Plex-backed identities.
When an LDAP client tries to log in, this service asks Plex to verify the username and password, and then returns an LDAP login success or failure. The LDAP side is only a read-only view of Plex. Passwords stay in Plex rather than being copied into a separate LDAP database.
What the service exposes
- An LDAPv3 listener for bind, search, and unbind operations
- A small HTTP server with health and readiness endpoints
- An in-memory LDAP tree built from the Plex owner account and matching shared users
- A single process that runs HTTP and LDAP listeners
Running in Docker
Install and run the published image directly from GHCR:
docker pull ghcr.io/archmonger/plex-ldap-gateway:latest
docker run -d `
--name plex-ldap-gateway `
-p 1389:1389 `
-p 7576:7576 `
-v "<your-preferred-data-path>:/config" `
-e PLEX_OWNER_TOKEN="<your-plex-owner-token>" `
-e PLEX_MACHINE_IDENTIFIER="<your-plex-machine-identifier>" `
ghcr.io/archmonger/plex-ldap-gateway:latest
Running locally
- Install Python 3.11 or higher on your machine.
- Open this repository's files on your local machine within terminal and set the required environment variables.
- Install the python project then start the bundled runner via the following commands:
pip install -e .
plex-ldap-gateway
Alternatively, you can also start the ASGI app through an external server:
uvicorn --factory plex_ldap_gateway.app:create_app --host 0.0.0.0 --port 7576
Environment variables
Required application settings:
PLEX_OWNER_TOKEN: Plex owner or admin token used to enumerate and authorize directory usersPLEX_MACHINE_IDENTIFIER: Plex Media ServermachineIdentifierused to scope directory access
Optional application settings:
PLEX_BASE_URL: defaults tohttps://plex.tvPLEX_CLIENT_IDENTIFIER: defaults toplex-ldap-gateway-{PLEX_MACHINE_IDENTIFIER}PLEX_CLIENT_PRODUCT: defaults toPlex LDAP GatewayPLEX_CLIENT_VERSION: defaults to the package versionPLEX_TIMEOUT_SECONDS: defaults to10GATEWAY_LDAP_STRICT_MACHINE_MATCH: defaults totrueGATEWAY_LDAP_REFRESH_SECONDS: defaults to300GATEWAY_LDAP_BASE_DN: defaults todc=plex,dc=ldapGATEWAY_LDAP_HOST: defaults to0.0.0.0GATEWAY_LDAP_PORT: defaults to1389GATEWAY_HTTP_HOST: defaults to0.0.0.0GATEWAY_HTTP_PORT: defaults to7576GATEWAY_LOG_LEVEL: defaults toERRORGATEWAY_LOG_OUTPUT: defaults toconsole; supported values areconsole,file, andbothGATEWAY_LOG_FILE_PATH: defaults toplex-ldap-gateway.log; used whenGATEWAY_LOG_OUTPUTincludes file output
Docker specific settings:
PUID,PGID,TZGATEWAY_LDAP_BIND_ADDRESS,GATEWAY_HTTP_BIND_ADDRESSSERVICE_CRASH_MAX_ATTEMPTS,SERVICE_CRASH_WINDOW_SECONDS,SERVICE_CRASH_BACKOFF_SECONDS
GATEWAY_LDAP_BIND_ADDRESS and GATEWAY_HTTP_BIND_ADDRESS only control host port publishing in compose.yml. They are not application settings. Inside the container, the service binds with GATEWAY_LDAP_HOST=0.0.0.0 and GATEWAY_HTTP_HOST=0.0.0.0.
Common client settings
Here is an example LDAP client settings, written from the perspective of the Jellyfin LDAP-Auth plugin:
| Setting | Value | Notes |
|---|---|---|
LDAP Server |
192.168.1.123 |
Replace this value with the host/IP that publishes the LDAP port. Can be set to a container name if on a shared Docker network. |
LDAP Port |
1389 |
Use the published port if you changed the default. |
Secure LDAP |
Disabled |
Turn on only when an external TLS terminator exposes LDAPS in front of the gateway. |
StartTLS |
Disabled |
This service does not advertise StartTLS. |
Allow Password Change |
Disabled |
This service directory is read-only. Passwords are managed by Plex. |
Password Reset Url |
https://app.plex.tv/auth/#?resetPassword |
Optional convenience link; password changes do not happen through LDAP here. |
LDAP Bind User |
blank | Leave blank to use anonymous binds. |
LDAP Bind User Password |
blank | Not needed when the bind user is blank. |
LDAP Base DN for searches |
ou=users,dc=plex,dc=ldap |
This should match the base DN used in the gateway configuration GATEWAY_LDAP_BASE_DN. |
LDAP Search Filter |
(objectClass=inetOrgPerson) |
Used as the base filter; the plugin adds an OR over the search attributes below. |
LDAP Search Attributes |
uid, cn, mail, plexUsername |
Allow users sign in with any username-like attribute. |
LDAP Uid Attribute |
uid |
Unique and always present. |
LDAP Username Attribute |
cn |
Default for Jellyfin usernames created from LDAP. Can be set to uid if you want to keep your new Plex-LDAP users completely separate. |
LDAP Password Attribute |
blank | This service does not support password changes, and this setting is only needed when an LDAP server supports password changes. |
Enable profile image synchronization |
Disabled |
This service does not expose a profile image attribute. |
Remove profile images not in LDAP |
Disabled |
This service does not expose a profile image attribute, thus this setting has no effect. |
LDAP Admin Base DN |
ou=users,dc=plex,dc=ldap |
Set to the value you put within LDAP Base DN for searches. Admins are not sorted by DN, but rather by the filter below. |
LDAP Admin Filter |
(employeeType=owner) |
Automatically grant Jellyfin admin access to the 'Plex owner' account. |
Enable Admin Filter 'memberUid' mode |
Disabled |
Not used by this directory layout. |
Enable User Creation |
Enabled |
Creates an equivalent Jellyfin user on first successful LDAP login. The new Jellyfin user will be auto-configured to authenticate against the LDAP server, rather than the local Jellyfin database. Note: If a user already exists with the same LDAP Username Attribute, they will not be automatically reconfigured to authenticate against LDAP. However, they will still be able to login if their credentials match. |
Technical overview
How the directory is built
- Load settings from environment variables.
- Fetch the Plex owner account with
PLEX_OWNER_TOKEN. - Fetch the Plex users shared by that owner.
- If
GATEWAY_LDAP_STRICT_MACHINE_MATCH=true, keep only users who can accessPLEX_MACHINE_IDENTIFIER. - Build a read-only LDAP tree rooted at
GATEWAY_LDAP_BASE_DN, with all users underou=users.
Binding behavior
Credentialed binds can resolve a user by:
- Full DN, such as
uid=alice,ou=users,dc=plex,dc=ldap - Plain identifier, such as
alice - Email address, such as
alice@example.com - Simple
attr=valueinput, such asuid=alice
Short-form identifiers only work when they map to exactly one generated user in the current directory snapshot.
An empty bind DN is treated as an anonymous bind. Non-anonymous binds with empty passwords are rejected.
LDAP layout
By default, the generated directory looks like this:
dc=plex,dc=ldap
+-- ou=users
+-- uid=<generated-user-id>
The root DN is configurable with GATEWAY_LDAP_BASE_DN. User entries always live under ou=users,<base DN>.
Each generated user entry is read-only and exposes these LDAP attributes:
| Attribute | Value | Notes |
|---|---|---|
objectClass |
top, person, organizationalPerson, inetOrgPerson |
Fixed for every generated user |
uid |
Generated from Plex username, email, title, or a fallback value | Also used in the LDAP DN |
cn |
Plex display name | Uses Plex title, then username, email, UUID, or plex-user |
sn |
Derived surname | Falls back to uid when a surname cannot be inferred |
displayName |
Same display name as cn |
Always present |
employeeType |
owner or shared |
Shows how the user entered the directory |
plexUsername |
Plex username | Present only when Plex returns one |
mail |
Plex email address | Present only when Plex returns one |
userPrincipalName |
Same value as mail |
Present only when Plex returns an email |
HTTP endpoints
GET /healthz: process status, directory size, last refresh timestamp, and listener stateGET /readyz: readiness state, refreshing from Plex when the cache is staleGET /readyz?force=1: forces a fresh Plex directory refresh before responding
Testing and CI
Useful local commands:
hatch fmt --check
hatch test -m "not live_plex"
hatch test --cover
Live Plex tests need these environment variables:
PLEX_OWNER_TOKENPLEX_MACHINE_IDENTIFIERPLEX_TEST_BIND_LOGINPLEX_TEST_BIND_PASSWORD
Optional live-test variables:
PLEX_TEST_BIND_IDENTITY: defaults toPLEX_TEST_BIND_LOGINPLEX_TEST_EXPECTED_USERNAMEPLEX_TEST_EXPECTED_EMAIL
Security and behavior notes
- LDAP simple bind sends credentials in cleartext unless you add transport security. Put this behind trusted network boundaries or wrap LDAP with TLS separately.
- The generated LDAP directory is read-only.
- Passwords are validated against Plex at bind time rather than stored in LDAP.
- The gateway runs within a single process and is limited to ~50,000 requests per second. It can be scaled horizontally behind a load balancer, if needed.
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file plex_ldap_gateway-0.2.1.tar.gz.
File metadata
- Download URL: plex_ldap_gateway-0.2.1.tar.gz
- Upload date:
- Size: 32.1 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.14
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
8632ec542bfbfbdb9178cb4fcda7328d2a5b7cd522b9975c66d38a3bc191cac0
|
|
| MD5 |
c68893f4a4f2479196958d82de796deb
|
|
| BLAKE2b-256 |
47066b85a30e5148dbd3a20c4ea79066a831dccabbe6372135322b3664008a69
|
Provenance
The following attestation bundles were made for plex_ldap_gateway-0.2.1.tar.gz:
Publisher:
publish-release-wheel.yml on Archmonger/Plex-LDAP-Gateway
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
plex_ldap_gateway-0.2.1.tar.gz -
Subject digest:
8632ec542bfbfbdb9178cb4fcda7328d2a5b7cd522b9975c66d38a3bc191cac0 - Sigstore transparency entry: 2232529030
- Sigstore integration time:
-
Permalink:
Archmonger/Plex-LDAP-Gateway@ff7ed47b477bf650717df54934a7e118e7271cba -
Branch / Tag:
refs/tags/0.2.1 - Owner: https://github.com/Archmonger
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish-release-wheel.yml@ff7ed47b477bf650717df54934a7e118e7271cba -
Trigger Event:
release
-
Statement type:
File details
Details for the file plex_ldap_gateway-0.2.1-py3-none-any.whl.
File metadata
- Download URL: plex_ldap_gateway-0.2.1-py3-none-any.whl
- Upload date:
- Size: 21.4 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.14
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
db9837e34d889623c17e971a7eb28b2de66d634e9203a21c98457e5c7a5fad04
|
|
| MD5 |
faca31cb8d44b72a126ca8dc11241903
|
|
| BLAKE2b-256 |
aa0f970d47983bbf35de2f03dce19721cb7b2a590fa464f067029079610fcd40
|
Provenance
The following attestation bundles were made for plex_ldap_gateway-0.2.1-py3-none-any.whl:
Publisher:
publish-release-wheel.yml on Archmonger/Plex-LDAP-Gateway
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
plex_ldap_gateway-0.2.1-py3-none-any.whl -
Subject digest:
db9837e34d889623c17e971a7eb28b2de66d634e9203a21c98457e5c7a5fad04 - Sigstore transparency entry: 2232529826
- Sigstore integration time:
-
Permalink:
Archmonger/Plex-LDAP-Gateway@ff7ed47b477bf650717df54934a7e118e7271cba -
Branch / Tag:
refs/tags/0.2.1 - Owner: https://github.com/Archmonger
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish-release-wheel.yml@ff7ed47b477bf650717df54934a7e118e7271cba -
Trigger Event:
release
-
Statement type: