Run a 389 Directory Server Instance as a Non-root User

Prepare a custom Directory Server root with dscreate ds-root, add administration wrapper scripts to PATH, create an unprivileged ldap-dev instance on ports above 1024, and manage it with user-specific dsctl commands.

Published

Updated

Read time 11 min read

Reviewed byDeepak Prasad

389 Directory Server non-root instance with custom dsroot directory, user-owned ns-slapd process, and LDAP ports above 1024

A non-root 389 Directory Server instance keeps configuration, databases, logs, and the ns-slapd process under a regular Linux account instead of the system dirsrv user.

Before you start:

This guide covers:

  • dscreate ds-root and wrapper scripts in $HOME/bin
  • Creating an instance on LDAP/LDAPS ports at or above 1024
  • Verifying the instance, locating files under $HOME/dsroot, and managing it with dsctl (no dirsrv@ systemd unit)
IMPORTANT
Not covered here: package installation, every interactive installer prompt, TLS hardening, firewall rules, or dsconf/dsidm references. See the linked chapters and dsctl / dsconf cheat sheets.

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


When should you use a non-root 389 Directory Server instance?

Official documentation positions non-root Directory Server mainly for testing and LDAP application development. Use it when you:

  • Build or debug an LDAP-aware application without root access
  • Need a disposable directory in CI, a classroom lab, or a shared host
  • Can write under $HOME but cannot create dirsrv@INSTANCE services with sudo

For production, prefer a root-managed instance under /etc/dirsrv and /var/lib/dirsrv with systemd and distribution security integration.

Requirement or limitation Details
Package installation Must already be completed by an administrator (389-ds-base or distribution equivalent)
LDAP and LDAPS ports Must be 1024 or higher for an ordinary unprivileged process—389 and 636 require extra privileges or forwarding
Instance files Stored under your custom Directory Server root (for example $HOME/dsroot)
Service ownership ns-slapd runs as your regular user, not dirsrv
SNMP Not supported for non-root instances
Firewall changes Opening listener ports may still require sudo or another administrator
SELinux labeling Automatic Directory Server SELinux port and file labeling is not performed; the installer skips semanage operations that require elevated privileges

The non-root installer does not disable SELinux system-wide. It skips Directory Server-specific file and port labeling because the regular user cannot run semanage or apply the required system labels. For how SELinux file contexts and restorecon work on labeled paths, see Ansible SELinux file contexts.


Prerequisites

Complete the Before you start items in the introduction, then log in as the unprivileged user who will own the instance.

As root or through sudo command, confirm packages are installed:

bash
command -v dscreate
output
/usr/sbin/dscreate

The result should point to the system-installed dscreate command. After ds-root, you use the $HOME/bin/dscreate wrapper instead.

Use a dedicated account such as ldapuser when you can. It keeps instance files out of a personal home directory. An administrator creates it with useradd -m -s /bin/bash ldapuser.

Log in as that user and confirm the account:

bash
id
output
uid=15003(ldapuser) gid=15003(ldapuser) groups=15003(ldapuser)

Your UID will differ, but the username and primary group should both identify the account that will own the instance.


Prepare the non-root Directory Server environment

dscreate ds-root prepares a private layout under your home directory:

  • Virtual root at $HOME/dsroot (etc, var, run, templates, ns-slapd copy)
  • Wrapper scripts in $HOME/bin for dscreate, dsctl, dsconf, and dsidm
  • Each wrapper sets PREFIX and INSTALL_PREFIX, then calls the system utility

Put $HOME/bin first on PATH for the current shell:

bash
export PATH="$HOME/bin:$PATH"

Make the change persistent in ~/.bash_profile so login shells pick it up:

bash
grep -qxF 'export PATH="$HOME/bin:$PATH"' ~/.bash_profile ||
    echo 'export PATH="$HOME/bin:$PATH"' >> ~/.bash_profile

Reload the profile so the current shell picks up the PATH change:

bash
source ~/.bash_profile

Run dscreate ds-root only after switching to the account that will own the instance. The command refuses to run as root:

bash
dscreate ds-root "$HOME/dsroot" "$HOME/bin"
Argument Example Role
Custom Directory Server root $HOME/dsroot Virtual prefix for etc, var, run, and instance data
Administration command directory $HOME/bin Directory receiving wrapper scripts (must be on PATH)

Bash may have cached the system dscreate path used to run ds-root. Clear that cache before you verify or create an instance:

bash
hash -r

Confirm all four wrappers are selected:

bash
command -v dscreate
output
/home/ldapuser/bin/dscreate

Confirm the dsctl wrapper is selected:

bash
command -v dsctl
output
/home/ldapuser/bin/dsctl

Confirm the dsconf wrapper is selected:

bash
command -v dsconf
output
/home/ldapuser/bin/dsconf

Confirm the dsidm wrapper is selected:

bash
command -v dsidm
output
/home/ldapuser/bin/dsidm

If the wrapper is not selected, the system dscreate command attempts to use the standard installation layout and the non-root setup normally fails with permission or path errors. The wrapper directory must appear in PATH before the standard administration tools.

List the wrappers installed under $HOME/bin:

bash
ls "$HOME/bin"
output
dsconf  dscreate  dsctl  dsidm

A wrapper redirects tools into your private root before calling the system binary:

text
#!/bin/sh
export PATH="/home/ldapuser/dsroot:$PATH"
export PREFIX="/home/ldapuser/dsroot"
export INSTALL_PREFIX="/home/ldapuser/dsroot"
exec /usr/sbin/dscreate "${@}"

Create the non-root instance

Pick values before you run dscreate interactive. Ports must be free on the host and at or above 1024.

Setting Example
Instance name ldap-dev
LDAP port 1389
LDAPS port 1636
Suffix dc=dev,dc=example,dc=com
Instance root $HOME/dsroot

On the tested 389 DS 3.2.0 installation, the non-root defaults begin with 1389 and 1636 and move to another available high-port pair when necessary. Treat these as convenience defaults: before accepting them, confirm that both ports are free and are 1024 or higher.

Review the values proposed by the installer and accept them only when both ports are free and 1024 or higher.

Only after the wrapper verification above succeeds, start the interactive installer:

bash
dscreate interactive

The first lines differ from a root-managed install. The installer warns that it cannot run semanage and asks you to continue without automatic SELinux labeling:

output
Install Directory Server (interactive mode)
===========================================
Non-privileged user cannot use semanage, will not relabel ports or files.

SELinux labels will not be applied, continue? [yes]:

Press Enter to accept yes at the SELinux prompt. The transcript below is abbreviated. I omitted password prompts, the LMDB size prompt, and prompts unrelated to the non-root layout.

Key non-root choices in interactive mode:

  • Instance name: ldap-dev (or another unique short name)
  • LDAP / LDAPS ports: 1389 / 1636 (or another free pair ≥ 1024)
  • Suffix: dc=dev,dc=example,dc=com
  • Create top suffix entry: yes; sample entries: no
  • Start after install: yes
output
Enter system's hostname [ldap1.example.com]:

Enter the instance name [ldap1]: ldap-dev

Enter port number [1389]: 1389

Create self-signed certificate database [yes]: yes

Enter secure port number [1636]: 1636

Enter Directory Manager DN [cn=Directory Manager]: cn=Directory Manager

Choose whether mdb or bdb is used. [mdb]: mdb

Enter the database suffix (or enter "none" to skip) [dc=ldap1,dc=example,dc=com]: dc=dev,dc=example,dc=com

Create sample entries in the suffix [no]: no

Create just the top suffix entry [no]: yes

Do you want to start the instance after the installation? [yes]: yes

Are you ready to install? [no]: yes
Starting installation ...
Validate installation settings ...
Create file system structures ...
Create self-signed certificate database ...
Create database backend: dc=dev,dc=example,dc=com ...
Perform post-installation tasks ...
Completed installation for instance: slapd-ldap-dev

The installer reports the on-disk name slapd-ldap-dev. For dsctl commands, use the short name ldap-dev.

NOTE
Alternative: dscreate from-file with an INF from dscreate create-template. set port and secure_port above 1024 in [slapd]. See dscreate INF file reference.

Verify the instance and custom paths

Run these checks in order:

bash
dsctl -l
output
slapd-ldap-dev

Confirm the new instance is running:

bash
dsctl ldap-dev status
output
Instance "ldap-dev" is running

Confirm ns-slapd runs as your user:

bash
pgrep -a -u "$USER" ns-slapd
output
25110 /usr/sbin/ns-slapd -D /home/ldapuser/dsroot/etc/dirsrv/slapd-ldap-dev -i /home/ldapuser/dsroot/run/dirsrv/slapd-ldap-dev.pid

The -D path points at your custom root, not /etc/dirsrv.

Check listeners on the ports you chose:

bash
ss -lntp | grep -E ':1389|:1636'
output
LISTEN 0      128                *:1636             *:*    users:(("ns-slapd",pid=25110,fd=9))
LISTEN 0      128                *:1389             *:*    users:(("ns-slapd",pid=25110,fd=8))

Test LDAP access against the suffix. Use -W so the password is not stored in shell history:

bash
ldapsearch -x -H ldap://localhost:1389 -D "cn=Directory Manager" -W -b "dc=dev,dc=example,dc=com" -s base

Enter the Directory Manager password when prompted. A successful base search returns the suffix entry:

output
# dev.example.com
dn: dc=dev,dc=example,dc=com
objectClass: top
objectClass: domain
dc: dev
description: dc=dev,dc=example,dc=com

# search result
search: 2
result: 0 Success

This command performs a simple bind over unencrypted LDAP on loopback only. For remote or untrusted networks:

  • Use StartTLS or LDAPS
  • Configure the client to trust the Directory Server CA
  • Do not copy the plain ldapsearch example for password authentication over the network

Non-root instances store everything under your custom Directory Server root:

text
$HOME/dsroot/
├── etc/dirsrv/slapd-ldap-dev/     # dse.ldif, certificates, certmap.conf
├── var/lib/dirsrv/slapd-ldap-dev/ # LMDB database (db/), backups (bak/), LDIF (ldif/)
├── var/log/dirsrv/slapd-ldap-dev/ # access, errors, audit logs
├── run/dirsrv/slapd-ldap-dev.pid  # PID file while running
└── etc/dirsrv/ssca/               # self-signed CA material for this private Directory Server root
Area Path under $HOME/dsroot
Configuration etc/dirsrv/slapd-ldap-dev/ (dse.ldif, TLS databases)
Database var/lib/dirsrv/slapd-ldap-dev/db/
Logs var/log/dirsrv/slapd-ldap-dev/ (errors, access, audit)
Certificates etc/dirsrv/slapd-ldap-dev/ (cert9.db, key4.db, Server-Cert.crt)
PID file run/dirsrv/slapd-ldap-dev.pid
Backup and LDIF directories var/lib/dirsrv/slapd-ldap-dev/bak/ and .../ldif/

The 389 Directory Server architecture chapter explains how instance name, suffix, and backend relate. This layout follows the same concepts inside your private root.


Manage the instance without systemd

Non-root instances are not managed with systemctl start dirsrv@ldap-dev. As the owning user, run:

bash
dsctl ldap-dev status
dsctl ldap-dev stop
dsctl ldap-dev start
dsctl ldap-dev restart

These commands use the $HOME/bin wrapper, not a dirsrv@ldap-dev systemd unit. See the dsctl command cheat sheet for backup, LDIF export, and removal.

  • No automatic start after reboot — run dsctl INSTANCE start manually as the owning user
  • If dsctl cannot access the instance, confirm command -v dsctl returns $HOME/bin/dsctl

Allow remote access securely

Local ldap://localhost:1389 success does not prove remote clients can connect. Before opening the instance to the network:

  • Listener address — confirm the instance listens where you expect; restrict interfaces with nsslapd-listenhost and nsslapd-securelistenhost through dsconf, then restart. Do not use nsslapd-localhost for binding—it names the host, not the listen interface. Change cn=config through dsconf or LDAP, not by editing dse.ldif on a running server.
  • Firewall — open TCP 1389 and 1636 when clients connect from other hosts (often needs an administrator)
  • Client URIs — include the nonstandard port (ldap://host:1389, ldaps://host:1636)
  • TLS — require encryption before password binds across untrusted networks; production hardening is in the TLS course chapter

Troubleshoot common errors

Symptom Likely cause Fix
Wrong dscreate or dsctl behavior System binaries used instead of wrappers Confirm command -v dscreate resolves to $HOME/bin/dscreate; run hash -r after ds-root
Permission denied creating files $HOME, $HOME/bin, or $HOME/dsroot not owned by your user Run ls -ld ~ ~/bin ~/dsroot and fix ownership with an administrator if needed
LDAP port cannot be opened Port below 1024, or port already in use Choose a free port ≥ 1024; check with ss -lntp
Instance missing from dsctl -l Wrong dsctl binary or different user Confirm command -v dsctl returns $HOME/bin/dsctl as the instance owner
Works locally, remote clients fail Firewall, routing, listener address, or wrong port in URI Open firewall ports, verify ss -lntp, give clients the correct nonstandard port
Tools stop working after login $HOME/bin not in persistent PATH Add export PATH="$HOME/bin:$PATH" to ~/.bash_profile and start a new session
ds-root fails immediately Command run as root Switch to the owning user; root receives ds-root subcommand should not be run by root user

During validation, dscreate reports an error when a port is privileged or busy:

output
Error: port 1389 is already in use, or missing NET_BIND_SERVICE

For ports above 1024, the usual cause is another service already listening. Pick a different free port or stop the conflicting process.

When ownership is suspect, all three paths should belong to the user running dscreate:

bash
ls -ld ~ ~/bin ~/dsroot
output
drwx------. 4 ldapuser ldapuser 4096 Jul 15 14:39 /home/ldapuser
drwxr-xr-x. 2 ldapuser ldapuser 4096 Jul 15 14:39 /home/ldapuser/bin
drwx------. 6 ldapuser ldapuser 4096 Jul 15 14:39 /home/ldapuser/dsroot

Remove the instance and private environment

Removal is destructive. Back up anything you need from $HOME/dsroot first.

  1. Stop and remove the instance:
bash
dsctl ldap-dev stop

Remove the instance data when you are sure nothing else depends on it:

bash
dsctl ldap-dev remove --do-it
output
Removing instance ...
Non-privileged user cannot use semanage, will not relabel ports or files.
...
Completed instance removal

The semanage warning may repeat during removal; that is expected.

  1. Delete the private environment when no other instances or data remain. Run this only when the prepared root contains no other instances. All instances created through the same wrapper set share that private root and its command wrappers:
bash
rm -rf "$HOME/dsroot" "$HOME/bin"/{dscreate,dsctl,dsconf,dsidm}
  1. Optional: after logging out of the account and confirming that no processes remain, an administrator can remove the dedicated test account with userdel -r ldapuser.

Summary

  1. Have an administrator install 389 Directory Server packages system-wide.
  2. Log in as a regular user and run dscreate ds-root to create $HOME/dsroot and administration wrappers in $HOME/bin.
  3. Run hash -r and confirm all four wrappers resolve under $HOME/bin.
  4. Create the instance with dscreate interactive (or from-file), using LDAP and LDAPS ports at or above 1024.
  5. Manage the running ns-slapd process with user-specific dsctl. Do not use systemctl dirsrv@INSTANCE.

What's Next


References


Frequently Asked Questions

1. Can a non-root 389 Directory Server instance bind to port 389 without extra privileges?

No. An ordinary unprivileged process cannot bind to ports below 1024. Use ports such as 1389 and 1636 unless an administrator has deliberately configured capabilities or port forwarding.

2. Do I need root to install 389 Directory Server packages for a non-root instance?

Yes. An administrator must install 389-ds-base system-wide first. The regular user then runs dscreate ds-root to prepare a private Directory Server root and install administration wrapper scripts under their home directory.

3. Can I manage a non-root instance with systemctl dirsrv@INSTANCE?

No. Non-root instances are not registered as dirsrv@ systemd units. Start, stop, and remove them with the user-specific dsctl wrapper from your prepared bin directory while logged in as the owning user.

4. Is a non-root 389 Directory Server instance suitable for production?

Official documentation positions non-root instances for testing and LDAP application development. A root-managed instance with standard paths, systemd integration, and SELinux labeling is preferable for most production deployments.
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 …