Run Multiple 389 Directory Server Instances on One Host

Plan, create, and manage multiple 389 Directory Server instances on one host with unique names, LDAP ports, suffixes, systemd units, and client profiles.

Published

Updated

Read time 15 min read

Reviewed byDeepak Prasad

Two independent 389 Directory Server ns-slapd instances on one host with separate LDAP ports, suffixes, and systemd services

One 389 Directory Server package can host several independent ns-slapd servers on the same machine. Each instance keeps its own configuration, databases, logs, certificate database, and dirsrv@INSTANCE service; binaries and the systemd template are shared.

Tested on: Rocky Linux 10.2; 389 Directory Server 3.2.0.

A typical two-instance layout on one host looks like this:

text
389 Directory Server package
├── Instance: ldap1
│   ├── LDAP port 389
│   ├── LDAPS port 636
│   └── Suffix: dc=example,dc=com
└── Instance: ldap2
    ├── LDAP port 1389
    ├── LDAPS port 1636
    └── Suffix: dc=lab,dc=example,dc=com

Each box under the package is a separate ns-slapd server. It is not a suffix inside a shared process.

Before you start:

IMPORTANT
This guide covers multiple root-owned instances on one host. It does not cover non-root instance layouts, replication between instances, or full dsctl, dsconf, or dsidm references.

When should you run multiple instances?

Multiple instances fit the same host when you need logically separate directories without provisioning another machine.

Scenario Why a second instance helps
Development, testing, and staging Same packages and tools, different data and policies
Application isolation Distinct schema extensions, ACIs, or naming contexts per app
Upgrade or migration rehearsal Exercise a new version or backend change against a copy
Application-level administrative separation Separate Directory Manager credentials, configuration, data, and logs while retaining one shared host-security boundary
Temporary lab environments Spin up an extra listener set, then remove it when finished

Separate hosts or containers are a better fit when you need:

  • Strong security isolation between directories
  • Large production workloads with independent resource limits
  • Unrelated operating-system maintenance windows
  • High-availability replication across distinct machines

This guide stays on the single-host, multi-instance pattern.


How multiple instances are isolated

389 Directory Server keeps instance-specific resources apart while sharing the installed software.

Component Instance-specific?
Instance name Yes
ns-slapd process Yes
Listener address-and-port Yes (must be unique per binding)
Configuration Yes
Database and indexes Yes
Logs Yes
Certificate/key database and server certificate Yes
Bootstrap self-signed CA infrastructure Shared by the host installation
systemd service enablement (dirsrv@INSTANCE) Yes
systemd service template ([email protected]) Shared
Installed binaries Shared
SELinux policy Shared

Key rules:

  • Instance names must be unique.
  • Listener address-and-port combinations must be unique.
  • When all instances listen on every interface, each needs different LDAP and LDAPS ports.
  • Suffixes can match or differ. Each instance has its own database files. Distinct suffixes (for example dc=example,dc=com vs dc=lab,dc=example,dc=com) simplify labs.

Advanced option: bind a listener to a specific local address with nsslapd-listenhost so two instances can share a port number on different IP addresses. That requires explicit DNS, firewall, and certificate planning. This guide uses all-interfaces listeners and non-default ports instead.


Plan the instances

Write the plan down before you run dscreate again. When both instances listen on all interfaces, the second cannot reuse ports 389 or 636 while the first holds them.

Setting First instance Second instance
Instance name ldap1 ldap2
LDAP port 389 1389
LDAPS port 636 1636
Backend userroot userroot
Suffix dc=example,dc=com dc=lab,dc=example,dc=com
Service dirsrv@ldap1 dirsrv@ldap2

The backend name userroot belongs to each instance separately. The [backend-userroot] section in an INF file creates a backend inside the instance being provisioned, not a shared database across instances.


Inspect existing instances and ports

Before you create ldap2, confirm:

  • Which instances already exist (dsctl -l)
  • Which ports ns-slapd already holds (ss -lntp)
  • Which dirsrv@* units are loaded (systemctl list-units)
  • That your planned ports are free (1389 and 1636 in this lab)

Strip the slapd- prefix from dsctl -l output when you pass the name to dsctl, dsconf, or dsidm.

List registered instances:

bash
dsctl -l

Sample output when only the first instance exists:

output
slapd-ldap1

The slapd- prefix is directory metadata; commands use the short name ldap1.

See which TCP ports ns-slapd processes already hold:

bash
ss -lntp | grep ns-slapd

Sample output with one instance listening on the default ports:

output
LISTEN 0 128 *:636 *:* users:(("ns-slapd",pid=17814,fd=9))
LISTEN 0 128 *:389 *:* users:(("ns-slapd",pid=17814,fd=8))

Those lines tell you 389 and 636 are taken. Plan different ports for the next instance.

List systemd template units for Directory Server:

bash
systemctl list-units 'dirsrv@*'

Sample output when only ldap1 exists:

output
UNIT                 LOAD   ACTIVE SUB     DESCRIPTION
  [email protected] loaded active running 389 Directory Server ldap1.

1 loaded units listed. Pass --all to see loaded but inactive units, too.

Each row represents a loaded instance unit. Confirm ACTIVE=active and SUB=running; add --all to include inactive units. After you add ldap2, this command lists a second row when that unit is loaded.

Confirm the planned second-instance ports are free before you create it:

bash
ss -lnt | grep -E ':1389|:1636'

When neither port is in use, the command prints nothing. Because grep found no match, an exit status of 1 is expected in this case. That is not a successful exit code 0.

A clearer check that prints an explicit result:

bash
if ss -lnt | grep -Eq ':(1389|1636)([[:space:]]|$)'; then echo "Port 1389 or 1636 is already in use"; else echo "Ports 1389 and 1636 are available"; fi

Sample output when the ports are free:

output
Ports 1389 and 1636 are available

The [[:space:]]|$ boundary avoids accidentally matching a longer port number such as 13890.

For more dsctl inspection commands, see the 389 dsctl commands cheat sheet.


Create the second instance

Use a dscreate INF file so you can review instance_name, ports, and suffix before anything is written to disk. Copy your first-instance INF and change only what must differ:

ini
[general]
full_machine_name = ldap1.example.com

[slapd]
instance_name = ldap2
port = 1389
secure_port = 1636

[backend-userroot]
suffix = dc=lab,dc=example,dc=com

Notes on the INF deltas for the second instance:

  • full_machine_name is the host identity, not the instance name. On one machine, both instances normally share the same value.
  • Keep the same Directory Manager password policy and certificate settings as ldap1 unless your design requires otherwise.
  • For a separate client-facing DNS name (for example ldap2.example.com): add the DNS alias, set full_machine_name accordingly, and issue a matching server certificate. Do not mix that design with the shared-hostname approach without completing DNS and TLS planning.

Full template, dry-run validation (dscreate from-file -n), and parameter reference: Create a 389 Directory Server instance with a dscreate INF file.

Create the instance from the INF file:

bash
dscreate from-file /root/ldap2.inf

Sample output:

output
Starting installation ...
Validate installation settings ...
Create file system structures ...
Create self-signed certificate database ...
Perform SELinux labeling ...
Create database backend: dc=lab,dc=example,dc=com ...
Perform post-installation tasks ...
Completed installation for instance: slapd-ldap2

The completion line confirms the new instance name. dscreate also enables and starts dirsrv@ldap2 when start = True in [general].


Verify both instances

Run these checks after dscreate finishes:

  • dsctl -l — both instance names appear
  • dsctl ldap1 status and dsctl ldap2 status — each reports running
  • systemctl list-units 'dirsrv@*' — both units show ACTIVE=active and SUB=running
  • ps -ef | grep '[n]s-slapd' — separate PIDs and config paths
  • ss -lntp | grep ns-slapd — each PID owns its own port pair
  • ldapsearch against each suffix on its assigned port

List instances again:

bash
dsctl -l

Sample output:

output
slapd-ldap1
slapd-ldap2

Both names confirm the second instance landed on disk alongside the first.

Check each instance with dsctl:

bash
dsctl ldap1 status

Sample output:

output
Instance "ldap1" is running

Repeat the status check for the second instance:

bash
dsctl ldap2 status

Sample output:

output
Instance "ldap2" is running

Confirm systemd sees both units:

bash
systemctl list-units 'dirsrv@*'

Sample output:

output
UNIT                 LOAD   ACTIVE SUB     DESCRIPTION
  [email protected] loaded active running 389 Directory Server ldap1.
  [email protected] loaded active running 389 Directory Server ldap2.

2 loaded units listed. Pass --all to see loaded but inactive units, too.

Confirm ACTIVE=active and SUB=running on each row before you treat an instance as healthy. Drill into one unit when you need the listener ports from the journal:

bash
systemctl status dirsrv@ldap1 --no-pager

Sample output:

output
[email protected] - 389 Directory Server ldap1.
     Active: active (running) since Wed 2026-07-15 14:19:30 IST; 24min ago
   Main PID: 17814 (ns-slapd)
     Status: "slapd started: Ready to process requests"
             └─17814 /usr/sbin/ns-slapd -D /etc/dirsrv/slapd-ldap1 -i /run/dirsrv/slapd-ldap1.pid

Jul 15 14:19:30 ldap1.example.com ns-slapd[17814]: ... Listening on All Interfaces port 389 for LDAP requests

The Active: active (running) line and the journal entry for port 389 belong to ldap1 only.

Repeat the same check for ldap2 to confirm its non-default listener ports:

bash
systemctl status dirsrv@ldap2 --no-pager

Sample output:

output
[email protected] - 389 Directory Server ldap2.
     Active: active (running) since Wed 2026-07-15 14:41:04 IST; 3min ago
   Main PID: 26435 (ns-slapd)
     Status: "slapd started: Ready to process requests"
             └─26435 /usr/sbin/ns-slapd -D /etc/dirsrv/slapd-ldap2 -i /run/dirsrv/slapd-ldap2.pid

Jul 15 14:41:04 ldap1.example.com ns-slapd[26435]: ... Listening on All Interfaces port 1389 for LDAP requests
Jul 15 14:41:04 ldap1.example.com ns-slapd[26435]: ... Listening on All Interfaces port 1636 for LDAPS requests

ldap2 reports ports 1389 and 1636. Those are not the defaults 389 and 636.

Inspect running processes. Each instance has its own ns-slapd command line with a distinct -D config path:

bash
ps -ef | grep '[n]s-slapd'

Sample output:

output
dirsrv 17814 1 0 14:19 ? 00:00:01 /usr/sbin/ns-slapd -D /etc/dirsrv/slapd-ldap1 -i /run/dirsrv/slapd-ldap1.pid
dirsrv 26435 1 0 14:41 ? 00:00:00 /usr/sbin/ns-slapd -D /etc/dirsrv/slapd-ldap2 -i /run/dirsrv/slapd-ldap2.pid

Map listeners to those PIDs:

bash
ss -lntp | grep ns-slapd

Sample output:

output
LISTEN 0 128 *:636  *:* users:(("ns-slapd",pid=17814,fd=9))
LISTEN 0 128 *:389  *:* users:(("ns-slapd",pid=17814,fd=8))
LISTEN 0 128 *:1636 *:* users:(("ns-slapd",pid=26435,fd=9))
LISTEN 0 128 *:1389 *:* users:(("ns-slapd",pid=26435,fd=8))

PID 17814 owns 389/636; PID 26435 owns 1389/1636. Match port to PID before you change firewall rules or client URIs.

Test each suffix on its own port with anonymous base-object search:

bash
ldapsearch -x -H ldap://127.0.0.1:389 -b "dc=example,dc=com" -s base "(objectclass=*)" dn

Sample output:

output
# example.com
dn: dc=example,dc=com

# numEntries: 1

Query the second instance suffix on port 1389:

bash
ldapsearch -x -H ldap://127.0.0.1:1389 -b "dc=lab,dc=example,dc=com" -s base "(objectclass=*)" dn

Sample output:

output
# lab.example.com
dn: dc=lab,dc=example,dc=com

# numEntries: 1

Each command returns exactly one suffix entry on the port you specified. That confirms clients must target the correct URI, not just the host name.


Allow remote access to the second instance

Local ldapsearch does not open nonstandard ports for remote clients. On firewalld, two details matter for the second instance:

  • The ldap / ldaps services cover ports 389 and 636 only. They do not cover 1389 or 1636.
  • Open the actual ports assigned to each instance.

Add the second-instance ports with firewalld:

bash
firewall-cmd --permanent --add-port={1389/tcp,1636/tcp}

That registers both ports in the permanent firewalld configuration.

bash
firewall-cmd --reload

Reload applies the permanent rules to the running firewall.

Verify the ports are listed:

bash
firewall-cmd --list-ports

Sample output:

output
1389/tcp 1636/tcp

Your list may include other ports. Confirm 1389/tcp and 1636/tcp appear before remote clients connect.

Opening 1389/tcp only makes plain LDAP or StartTLS reachable; it does not encrypt the connection automatically. Do not send Directory Manager or user passwords over port 1389 without StartTLS. Prefer trusted LDAPS on port 1636 or configure StartTLS before remote simple binds. Opening firewall ports is a connectivity step, not TLS protection.

  • dscreate labels SELinux ports during creation.
  • If you change listener ports later with dsconf, assign the new ports the ldap_port_t type before restart — see the troubleshooting table.

Understand instance-specific files and services

Every instance name expands into its own directory tree and service unit. Stopping, restarting, or troubleshooting always uses that instance name. It does not use a shared global service.

Purpose First instance Second instance
Configuration /etc/dirsrv/slapd-ldap1/ /etc/dirsrv/slapd-ldap2/
Database /var/lib/dirsrv/slapd-ldap1/ /var/lib/dirsrv/slapd-ldap2/
Logs /var/log/dirsrv/slapd-ldap1/ /var/log/dirsrv/slapd-ldap2/
Service dirsrv@ldap1 dirsrv@ldap2

A restart of dirsrv@ldap2 does not interrupt ldap1 because systemd manages separate units backed by separate processes.


Manage each instance independently

Pass the instance name to select which server you affect:

  • dsctl — local/offline work: status, backup, LDIF export, removal
  • dsconf — online config via LDAPI/SASL EXTERNAL (local instance name) or LDAP/LDAPS (URI)
  • dsidm — directory data inside the named instance

Restart one instance without touching the other:

bash
dsctl ldap2 restart

Sample output:

output
Instance "ldap2" has been restarted

Read online configuration from each running server with dsconf:

bash
dsconf ldap1 config get nsslapd-port nsslapd-secureport nsslapd-localhost

Sample output:

output
nsslapd-port: 389
nsslapd-secureport: 636
nsslapd-localhost: ldap1.example.com

Those values belong to ldap1 only. Query ldap2 the same way:

bash
dsconf ldap2 config get nsslapd-port nsslapd-secureport nsslapd-localhost

Sample output:

output
nsslapd-port: 1389
nsslapd-secureport: 1636
nsslapd-localhost: ldap1.example.com

Both instances report the same host identity (ldap1.example.com); only the listener ports differ.

Command references: dsctl, dsconf, dsidm cheat sheets.


Configure separate connection profiles

Separate ~/.dsrc sections reduce mistakes about ports, suffixes, and bind identities. Tools select the [INSTANCE] section that matches the instance name you pass to dsconf or dsidm.

Create or update a profile for ldap2:

bash
dsctl ldap2 dsrc create --uri ldap://127.0.0.1:1389 --basedn dc=lab,dc=example,dc=com --binddn "cn=Directory Manager" --pwdfile /root/dm.pw --do-it

Sample output:

output
Updating "/root/.dsrc" with:

    [ldap2]
    uri = ldap://127.0.0.1:1389
    basedn = dc=lab,dc=example,dc=com
    binddn = cn=Directory Manager
    pwdfile = /root/dm.pw
Successfully updated: /root/.dsrc

Display the profiles you maintain:

bash
dsctl ldap2 dsrc display

Sample output:

output
[ldap1]
basedn = dc=example,dc=com
binddn = cn=Directory Manager
pwdfile = /root/dm.pw

[ldap2]
uri = ldap://127.0.0.1:1389
basedn = dc=lab,dc=example,dc=com
binddn = cn=Directory Manager
pwdfile = /root/dm.pw

Profile tips:

  • Give ldap1 its own section with uri = ldap://127.0.0.1:389 and matching basedn.
  • Use separate password files when Directory Manager passwords differ; /root/dm.pw assumes both instances share the same password.
  • Protect password files with chmod 600; see the chmod command for octal modes.

Plan resources and security

Each instance allocates its own memory, disk, file descriptors, and certificate database. Instances on one host are not a strong security boundary. root on the host can reach every instance's files, certificate database, LDAPI socket, and service.

Concern What to plan per instance
Memory LMDB cache and worker settings are per instance—not shared
Disk Database, logs, and backups grow independently under each slapd-INSTANCE tree
File descriptors Connection peaks on one instance do not borrow from another, but the host kernel limit is still shared
TLS Self-signed or CA-issued certificates and nsslapd-localhost values are per instance
Firewall Open only the nonstandard LDAP/LDAPS ports clients need—1389/1636 will not match a rule that allows 389 alone
Monitoring Track each dirsrv@INSTANCE unit, error log, and disk use separately
Host capacity Skip extra instances when CPU, RAM, or I/O are already saturated

Backups are also instance-specific. See the next section and the dedicated backup and restore chapter before you rely on multi-instance layouts in production.


Back up or remove one instance safely

Backups target one instance at a time. There is no cluster-wide backup on the host. Export or back up ldap2 before you change or remove it, even when ldap1 must stay online.

Before removal:

  • Confirm the instance name with dsctl -l
  • Export LDIF or run a backup for that instance only
  • Verify no application still connects to its LDAP or LDAPS ports
  • Remove only the intended instance

dsctl defaults to a dry run so you do not delete the wrong server by accident:

bash
dsctl ldap2 remove

Sample output:

output
Not removing: if you are sure, add --do-it

Adding --do-it:

  • Deletes that instance's configuration, database, logs, and certificate database
  • Removes the instance-specific systemd enablement link under dirsrv.target.wants
  • Does not uninstall the shared package or touch other instances

Treat removal as destructive. Full procedures: backup and restore and the dsctl cheat sheet.


Troubleshoot multiple-instance problems

Symptom Likely cause Fix
Instance already exists during dscreate The chosen instance_name is already on disk Run dsctl -l; pick a new name or remove the old test instance with dsctl INSTANCE remove --do-it when you intend to recreate it
LDAP or LDAPS port bind failure Another instance or service owns the port on the same address Run ss -lntp; assign free port and secure_port values in the INF file, or bind instances to different local addresses
SELinux denial after changing listener ports New ports not labeled ldap_port_t Assign the SELinux port type for the new LDAP/LDAPS ports before restart; see Red Hat Directory Server administration guidance
dsconf or dsidm changes the wrong directory Wrong instance name, URI, port, or .dsrc section Pass the correct instance name; set --uri in .dsrc when ports differ; verify basedn matches the target suffix
One instance starts but another fails Instance-specific config, SELinux, or certificate error Inspect that unit only: journalctl -u dirsrv@ldap2; read /var/log/dirsrv/slapd-ldap2/errors
Application reads stale or unexpected entries Client points at the wrong port or suffix Confirm LDAP URI includes the instance port; search the expected base DN; check TLS certificate identity for LDAPS

When ldap2 fails while ldap1 stays healthy, start with the failing unit's journal through journalctl. Shared package problems are rare compared with per-instance port or config mistakes:

bash
journalctl -u dirsrv@ldap2 --no-pager -n 8

Sample output:

output
Jul 15 14:41:04 ldap1.example.com ns-slapd[26435]: ... slapd started.  Listening on All Interfaces port 1389 for LDAP requests
Jul 15 14:41:04 ldap1.example.com ns-slapd[26435]: ... Listening on All Interfaces port 1636 for LDAPS requests
Jul 15 14:41:04 ldap1.example.com systemd[1]: Started [email protected] - 389 Directory Server ldap2..

Look for Listening on All Interfaces port lines that confirm the intended LDAP and LDAPS ports. A bind error that mentions an address already in use means you need free ports in the INF file or must stop the conflicting listener.


Summary

Running multiple 389 Directory Server instances on one host is supported and common in labs:

  • Install the package once; create each instance with dscreate.
  • Give every instance a unique name; when all instances listen on every interface, assign unique LDAP and LDAPS ports.
  • Keep configuration, databases, logs, certificate databases, and systemd enablement separate; share only package-level infrastructure.
  • Pass the instance name to dsctl, dsconf, and dsidm; use .dsrc profiles when ports and suffixes differ.
  • Open nonstandard listener ports in the host firewall before remote clients connect.
  • Back up an instance before you remove it. Deletion is limited to that instance but is not reversible without a backup.

What's Next


References


Frequently Asked Questions

1. Can two 389 Directory Server instances on one host use the same LDAP port?

Not when both instances listen on the same IP address or on all interfaces. In the common single-address configuration, the second instance must use different ports such as 1389 and 1636. Advanced deployments can bind instances to separate local IP addresses through nsslapd-listenhost, but that requires explicit listener, DNS, firewall, and certificate planning.

2. Can multiple instances share the same directory suffix?

Yes. Each instance is an independent ns-slapd server with its own database files, so two instances can use the same suffix DN without sharing data. For labs and application isolation, distinct suffixes are usually easier to manage.

3. Do I need to install 389 Directory Server again for a second instance?

No. One package installation provides the shared binaries. You create each additional instance with dscreate and manage it with dsctl, dsconf, and dsidm using the instance name.

4. How do I remove one instance without affecting the others?

Back up the instance first, confirm the instance name and ports, then run dsctl INSTANCE remove --do-it. That deletes only that instance configuration, database, logs, and certificate database—not the shared package, systemd template, or other instances.
Deepak Prasad

R&D Engineer

Founder of GoLinuxCloud with more than 15 years of expertise in Linux, Python, Go, Laravel, DevOps, Kubernetes, Git, Shell scripting, OpenShift, AWS, Networking, and Security. With extensive …