Configuration Guide
Complete guide to configuring LDAP Manager for your environment. The examples end with the committedconfig.example.yml, rendered in full at the end of this page.
Configuration Approaches
LDAP Manager supports two configuration approaches:- Minimal Configuration - Quick setup with auto-detection (recommended for beginners)
- Custom Configuration - Full control over forms and columns (for advanced users)
Minimal Configuration (Recommended)
The simplest way to get started. The UI automatically detects and shows common LDAP attributes.Single Node Example
clusters:
- name: "Production LDAP"
host: "ldap.company.com"
port: 389
bind_dn: "cn=Manager,dc=company,dc=com"
base_dn: "dc=company,dc=com" # Optional - auto-discovered if omitted
description: "Main production LDAP server"
readonly: false
Multiple Clusters
clusters:
- name: "Production LDAP"
host: "ldap.company.com"
port: 389
bind_dn: "cn=admin,dc=company,dc=com"
base_dn: "dc=company,dc=com"
description: "Production environment"
readonly: false
- name: "Development LDAP"
host: "ldap-dev.company.com"
port: 389
bind_dn: "cn=admin,dc=dev,dc=company,dc=com"
base_dn: "dc=dev,dc=company,dc=com"
description: "Development environment"
readonly: false
ℹ️
Note: What you get automatically:
- Users table: Username, Full Name, Email, Type
- Groups table: Group Name, Description, Members
- OUs table: OU Name, Description, DN
- Users can click "Columns" button to show/hide additional attributes
Multi-Master Cluster Configuration
For multi-master LDAP clusters with multiple nodes:clusters:
- name: "LDAP Cluster"
description: "3-node multi-master cluster"
nodes:
- host: "ldap1.company.com"
port: 389
name: "node1"
- host: "ldap2.company.com"
port: 389
name: "node2"
- host: "ldap3.company.com"
port: 389
name: "node3"
bind_dn: "cn=Manager,dc=company,dc=com"
base_dn: "dc=company,dc=com"
readonly: false
Features:
- Health monitoring for all nodes
- Replication topology visualization
- Automatic failover to healthy nodes
- Sync status monitoring
Docker Host Connection
For connecting to LDAP running on the Docker host machine:Docker Desktop (Mac/Windows)
clusters:
- name: "Local Docker LDAP"
host: "host.docker.internal"
port: 389
bind_dn: "cn=Manager,dc=example,dc=com"
Linux Docker
clusters:
- name: "Local Docker LDAP"
host: "172.17.0.1" # Default Docker bridge IP
port: 389
bind_dn: "cn=Manager,dc=example,dc=com"
User Creation Form Configuration
Define custom user creation forms with auto-fill fields and validation.Basic User Creation Form
clusters:
- name: "Corporate LDAP"
host: "ldap.company.com"
port: 389
bind_dn: "cn=admin,dc=company,dc=com"
user_creation_form:
base_ou: "ou=People,dc=company,dc=com"
object_classes:
- inetOrgPerson
- posixAccount
fields:
- name: uid
label: Username
type: text
required: true
- name: cn
label: Full Name
type: text
required: true
- name: mail
label: Email
type: email
required: true
auto_generate: "${uid}@company.com"
- name: userPassword
label: Password
type: password
required: true
Advanced Form with Auto-Generation
user_creation_form:
base_ou: "ou=People,dc=company,dc=com"
object_classes:
- inetOrgPerson
- posixAccount
- shadowAccount
fields:
- name: uid
label: Username
type: text
required: true
- name: cn
label: Full Name
type: text
required: true
- name: sn
label: Last Name
type: text
required: true
- name: givenName
label: First Name
type: text
required: true
- name: mail
label: Email
type: email
required: true
auto_generate: "${uid}@company.com"
- name: userPassword
label: Password
type: password
required: true
- name: uidNumber
label: UID Number
type: number
required: true
auto_generate: "next_uid"
readonly: true
- name: gidNumber
label: Group ID
type: number
required: true
default: 100
- name: homeDirectory
label: Home Directory
type: text
required: true
auto_generate: "/home/${uid}"
- name: loginShell
label: Shell
type: text
required: true
default: "/bin/bash"
Field Types
| Type | Description | Example |
|---|---|---|
text | Single-line text input | Username, Name |
email | Email with validation | user@example.com |
password | Password input (hidden) | User password |
number | Numeric input | UID, GID |
select | Dropdown selection | Department, Role |
Auto-Generate Options
| Value | Description | Example |
|---|---|---|
${uid}@company.com | Template with variable substitution | john@company.com |
next_uid | Auto-generate next available UID | 1001, 1002, 1003... |
/home/${uid} | Path with variable | /home/john |
Table Columns Configuration
Control which columns are visible by default in tables.Users Table Columns
table_columns:
users:
- name: uid
label: Username
default_visible: true
- name: cn
label: Full Name
default_visible: true
- name: mail
label: Email
default_visible: true
- name: uidNumber
label: UID
default_visible: false # Hidden by default
- name: gidNumber
label: GID
default_visible: false # Hidden by default
- name: homeDirectory
label: Home Directory
default_visible: false # Hidden by default
- name: loginShell
label: Shell
default_visible: false # Hidden by default
- name: objectClass
label: Type
default_visible: true
Groups Table Columns
groups:
- name: cn
label: Group Name
default_visible: true
- name: description
label: Description
default_visible: true
- name: members
label: Members
default_visible: true
- name: gidNumber
label: GID
default_visible: false
- name: dn
label: DN
default_visible: false
OUs Table Columns
ous:
- name: ou
label: OU Name
default_visible: true
- name: description
label: Description
default_visible: true
- name: dn
label: DN
default_visible: true
ℹ️
Note: Note: Users can always customize column visibility via the "Columns" button (⚙️) in the UI. The default_visible setting only controls the initial state.
Custom Schema Example
Full example with custom objectClass and attributes:
clusters:
- name: "Custom Schema LDAP"
host: "ldap.example.com"
port: 389
bind_dn: "cn=Manager,dc=example,dc=com"
base_dn: "dc=example,dc=com"
readonly: false
user_creation_form:
base_ou: "ou=People,dc=example,dc=com"
object_classes:
- inetOrgPerson
- posixAccount
- CustomEmployee # Custom objectClass
fields:
- name: uid
label: Username
type: text
required: true
- name: cn
label: Full Name
type: text
required: true
- name: mail
label: Email
type: email
required: true
- name: userPassword
label: Password
type: password
required: true
# Custom attributes
- name: department
label: Department
type: select
required: true
options:
- Engineering
- Sales
- Marketing
- name: employeeID
label: Employee ID
type: text
required: true
- name: location
label: Location
type: text
required: false
table_columns:
users:
- name: uid
label: Username
default_visible: true
- name: cn
label: Full Name
default_visible: true
- name: mail
label: Email
default_visible: true
- name: department
label: Department
default_visible: true # Custom attribute
- name: employeeID
label: Employee ID
default_visible: true # Custom attribute
- name: location
label: Location
default_visible: false # Custom attribute (hidden)
TLS / LDAPS
Per cluster. The certificate paths are inside the container.
tls:
mode: ldaps # none (default) | ldaps | starttls
ca_file: /certs/ca.crt
verify: true # false accepts any certificate - lab only
# cert_file: /certs/client.crt # mutual TLS
# key_file: /certs/client.key
# mount the certificates
docker run -d -v ./certs:/certs:ro ldap-manager:latest
mode: ldaps with a configured port of 389 is raised to 636 (logged). Full details:
TLS & LDAPS.
cn=config credential (Schema and ACI editors)
The data bind DN normally cannot read cn=config, so clusters using the Schema or ACI
editors need a second credential:
config:
bind_dn: "cn=config"
credential:
source: env # LDAP_MANAGER_CONFIG_<CLUSTER>_PASSWORD
Source may be stored, env, file or config, exactly like the data credential.
Without this block the editors report a clear message and nothing else is affected.
Context Path Configuration
Serve LDAP Manager under a custom base path (e.g.,/ldap-manager/) for integration with other applications.
Production Deployment
# Build and run with context path
CONTEXT_PATH=/ldap-manager docker compose -f docker-compose.prod.yml up -d
# Access at
http://localhost:8000/ldap-manager/
Development Mode
# Run with context path
CONTEXT_PATH=/ldap-manager docker compose up
# Access at
http://localhost:5173/ldap-manager/
Nginx Reverse Proxy
location /ldap-manager/ {
proxy_pass http://ldap-manager:8000/ldap-manager/;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
}
ℹ️
Note: Note: For integration with IAM/Auth applications, configure your reverse proxy to forward the context path to LDAP Manager. The parent app handles authentication while LDAP Manager handles directory management.
Environment Variables
Configure LDAP Manager behavior using environment variables in docker-compose.yml:
services:
ldap-manager:
image: vibhuvioio/ldap-manager:latest
ports:
- "5173:5173"
- "8000:8000"
volumes:
- ./config.yml:/app/config.yml
environment:
- PYTHONUNBUFFERED=1
- VITE_FOOTER_TEXT=LDAP Manager • My Company
- CONTEXT_PATH=/ldap-manager # Optional: custom base path
Available Variables
| Variable | Description | Default |
|---|---|---|
PYTHONUNBUFFERED | Python output buffering | 1 |
VITE_FOOTER_TEXT | Custom footer text (HTML allowed) | LDAP Manager |
CONTEXT_PATH | Custom base path for app | / |
Configuration Best Practices
Start Simple
- Begin with minimal configuration
- Add custom forms only when needed
- Let UI auto-detect columns initially
- Customize based on user feedback
Security
-
Use
readonly: truefor read-only access - Restrict bind DN permissions
- Use strong passwords
- Enable TLS/SSL in production
Performance
-
Specify
base_dnto avoid auto-discovery overhead - Limit visible columns to improve load times
- Use server-side pagination for large directories
Complete Example
config.example.yml is the maintained, complete example - fetched from the repository at
build time, so the panel cannot drift from the code. config.minimal.yml is the smallest
working config: auto-detection, no custom forms or columns.
Project Files
ldap-manager
Explorer
config.example.yml
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
# --------------------------------------------------------------------------- # Authentication (optional) # --------------------------------------------------------------------------- # Choose how people sign in to LDAP Manager itself. Three modes: # # none No login. Use this when nginx / oauth2-proxy / your SSO already # authenticates users before they reach the app. Visitors get # `default_role` below, which is READONLY unless you change it, so a # direct hit on the app cannot modify anything. # # local Built-in accounts, no external integration. On first start the UI # shows a one-time wizard to create them; passwords are hashed with # scrypt and stored encrypted in the container. # # ldap One of the clusters below authenticates your users; every other # cluster stays a cluster to manage. Users sign in with their own # directory password, so no cluster-wide bind secret is needed to log # in, and their role comes from group membership. # # If you omit this whole block the app runs in `none` mode with the `readonly` # default role, i.e. view-only. auth: mode: none # none | local | ldap # Role for a visitor who is not signed in. Only meaningful for mode: none. # Set to "admin" when you trust your reverse proxy and want full access. default_role: readonly # readonly | readwrite | admin session: lifetime_hours: 12 # how long a sign-in lasts # ---- only used when mode: ldap -------------------------------------------- # Uncomment this block and set mode: ldap above, so one of your clusters # authenticates users while the rest stay clusters to manage. # # ldap: # cluster: "example-cluster" # which cluster authenticates # # # Option A: build the user's DN from the username # user_dn_template: "uid={username},ou=People,dc=example,dc=com" # # # Where to search for group membership. Defaults to the cluster's base_dn. # user_base_dn: "dc=example,dc=com" # # # NOTE: authentication binds as the user's own DN, built only from # # user_dn_template (or `uid=<username>,<user_base_dn>` when it is unset). # # `user_filter` is accepted in the schema but is NOT used by the login # # path, so a config relying on it will silently fall back to the uid= form. # # role_map: # # Role for anyone whose groups match nothing below. # default: readonly # # LDAP group DN -> role. member, uniqueMember and memberUid are all # # supported, so groupOfNames, groupOfUniqueNames and posixGroup work. # groups: # "cn=ldap-admins,ou=Group,dc=example,dc=com": admin # "cn=ldap-ops,ou=Group,dc=example,dc=com": readwrite # --------------------------------------------------------------------------- # Clusters # --------------------------------------------------------------------------- clusters: - name: "example-cluster" host: "ldap.example.com" port: 389 bind_dn: "cn=admin,dc=example,dc=com" base_dn: "dc=example,dc=com" description: "Example LDAP cluster" readonly: false # How the bind password for THIS cluster is supplied. Your choice per cluster. # LDAP Manager never stores a password: it reads the secret from one of these # sources, and there is no UI or API that writes one. # # env Read from an environment variable, so the secret never touches # disk. Defaults to LDAP_MANAGER_CLUSTER_<NAME>_PASSWORD. # file Read from a mounted secret file - a Docker/Kubernetes secret. # config Plaintext `bind_password` right here. Fine for a lab; the app # logs a warning because the secret sits in the config file. # # Omit this block entirely and the cluster uses `env`. credential: source: env env: LDAP_MANAGER_CLUSTER_EXAMPLE_PASSWORD # only for source: env # file: /run/secrets/example-cluster # only for source: file # bind_password: "change-me" # only for source: config # Optional: TLS for this cluster's connections. # none (default) plain ldap:// # ldaps TLS from the first byte (ldaps://). With no explicit port, a # configured port of 389 is raised to 636. # starttls connect on ldap:// then upgrade with StartTLS. # # ca_file/cert_file/key_file are paths INSIDE the ldap-manager container, so # mount the certificates there (e.g. -v ./certs:/certs:ro). # # verify: true (default) checks the server certificate against ca_file. # false accepts any certificate - lab use only. # # Omit this block entirely and the cluster uses plaintext. # tls: # mode: ldaps # ca_file: /certs/ca.crt # verify: true # # cert_file: /certs/client.crt # mutual TLS # # key_file: /certs/client.key # Optional: cn=config connection, needed by the Schema and ACI editors. # The data bind DN usually cannot read cn=config, so give the config admin # its own DN and credential. # config: # bind_dn: "cn=config" # credential: # source: env # LDAP_MANAGER_CONFIG_EXAMPLE_PASSWORD # Optional: Define user creation form (no-code form builder) # See https://vibhuvioio.com/ldap-manager/configuration for details user_creation_form: base_ou: "ou=People,dc=example,dc=com" object_classes: - inetOrgPerson - posixAccount - top - shadowAccount fields: - name: uid label: Username type: text required: true placeholder: "e.g., jdoe" - name: cn label: First Name type: text required: true - name: sn label: Last Name type: text required: true - name: mail label: Email type: email required: true auto_generate: "${uid}@example.com" # Template: ${uid} replaced with username - name: userPassword label: Password type: password required: true - name: uidNumber label: UID Number type: number required: true auto_generate: "next_uid" # Auto-generates next available UID starting at 2000 readonly: true - name: gidNumber label: Primary Group ID type: number required: true default: 100 - name: homeDirectory label: Home Directory type: text required: true auto_generate: "/home/${uid}" # Template: ${uid} replaced with username - name: loginShell label: Login Shell type: text required: true default: "/bin/bash" - name: shadowLastChange label: Shadow Last Change type: number required: false auto_generate: "days_since_epoch" # Auto-generates days since 1970-01-01 readonly: true - name: shadowMax label: Shadow Max type: number required: false default: 99999 - name: shadowWarning label: Shadow Warning type: number required: false default: 7 # Multi-master cluster example - name: "multi-master-cluster" nodes: - host: "ldap1.example.com" port: 389 - host: "ldap2.example.com" port: 389 - host: "ldap3.example.com" port: 389 bind_dn: "cn=admin,dc=example,dc=com" base_dn: "dc=example,dc=com" description: "Multi-master LDAP cluster" readonly: false # Same env-sourced secret every node shares. credential: source: env env: LDAP_MANAGER_CLUSTER_MULTI_MASTER_PASSWORD # Optional: Customize table columns # Control which columns appear in Users/Groups/OUs tables # Users can show/hide columns via the "Columns" button in the UI # See https://vibhuvioio.com/ldap-manager/configuration table_columns: users: - name: uid label: Username default_visible: true # Show by default - name: cn label: Full Name default_visible: true - name: mail label: Email default_visible: true - name: uidNumber label: UID default_visible: false # Hidden by default (technical field) - name: gidNumber label: GID default_visible: false # Hidden by default (technical field) - name: homeDirectory label: Home Directory default_visible: false # Hidden by default (technical field) - name: loginShell label: Shell default_visible: false # Hidden by default (technical field) - name: objectClass label: Type default_visible: true # Show user type badge groups: - name: cn label: Group Name default_visible: true - name: description label: Description default_visible: true - name: gidNumber label: GID default_visible: false - name: members label: Members default_visible: true - name: dn label: DN default_visible: false ous: - name: ou label: OU Name default_visible: true - name: description label: Description default_visible: true - name: dn label: DN default_visible: true
YAMLUTF-8
Ln 2722 files
config.example.yml is the reference for clusters, user_creation_form and
table_columns. The tls and config blocks above are per-cluster keys - add them to a
cluster in the same file.
The auth: block at the top of the file is not per-cluster: it decides how people sign in to
LDAP Manager itself. All three modes are documented on
Authentication Modes.
Next Steps
-
Authentication Modes -
none,localorldap - Compatibility - what your server must have enabled
- TLS & LDAPS - certificate modes per cluster
- Security - what each role can do