Configuration Guide

Complete guide to configuring LDAP Manager for your environment. The examples end with the committed config.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)
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

TypeDescriptionExample
textSingle-line text inputUsername, Name
emailEmail with validationuser@example.com
passwordPassword input (hidden)User password
numberNumeric inputUID, GID
selectDropdown selectionDepartment, Role

Auto-Generate Options

ValueDescriptionExample
${uid}@company.comTemplate with variable substitutionjohn@company.com
next_uidAuto-generate next available UID1001, 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

VariableDescriptionDefault
PYTHONUNBUFFEREDPython output buffering1
VITE_FOOTER_TEXTCustom footer text (HTML allowed)LDAP Manager
CONTEXT_PATHCustom 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: true for read-only access
  • Restrict bind DN permissions
  • Use strong passwords
  • Enable TLS/SSL in production

Performance

  • Specify base_dn to 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