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.
Release files for plex-ldap-gateway 0.2.1
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| plex_ldap_gateway-0.2.1.tar.gz | 32.1 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| plex_ldap_gateway-0.2.1-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 53.5 kB
Release files / plex_ldap_gateway-0.2.1.tar.gz
| Download URL | plex_ldap_gateway-0.2.1.tar.gz |
|---|---|
| Size | 32.1 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
8632ec542bfbfbdb9178cb4fcda7328d2a5b7cd522b9975c66d38a3bc191cac0
|
|
BLAKE2b-256 checksum How to use checksums |
47066b85a30e5148dbd3a20c4ea79066a831dccabbe6372135322b3664008a69
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/6.1.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Jul 24, 2026.
Transparency logRelease files / plex_ldap_gateway-0.2.1-py3-none-any.whl
| Download URL | plex_ldap_gateway-0.2.1-py3-none-any.whl |
|---|---|
| Size | 21.4 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
db9837e34d889623c17e971a7eb28b2de66d634e9203a21c98457e5c7a5fad04
|
|
BLAKE2b-256 checksum How to use checksums |
aa0f970d47983bbf35de2f03dce19721cb7b2a590fa464f067029079610fcd40
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/6.1.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Jul 24, 2026.
Transparency log