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:
- Install 389 Directory Server — packages must be installed by an administrator; includes the full interactive install guide for root-managed instances
- dscreate INF file reference — repeatable system-wide setup with an answer file
This guide covers:
dscreate ds-rootand 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 withdsctl(nodirsrv@systemd unit)
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
$HOMEbut cannot createdirsrv@INSTANCEservices withsudo
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:
command -v dscreate/usr/sbin/dscreateThe 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:
iduid=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-slapdcopy) - Wrapper scripts in
$HOME/binfordscreate,dsctl,dsconf, anddsidm - Each wrapper sets
PREFIXandINSTALL_PREFIX, then calls the system utility
Put $HOME/bin first on PATH for the current shell:
export PATH="$HOME/bin:$PATH"Make the change persistent in ~/.bash_profile so login shells pick it up:
grep -qxF 'export PATH="$HOME/bin:$PATH"' ~/.bash_profile ||
echo 'export PATH="$HOME/bin:$PATH"' >> ~/.bash_profileReload the profile so the current shell picks up the PATH change:
source ~/.bash_profileRun dscreate ds-root only after switching to the account that will own the instance. The command refuses to run as root:
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:
hash -rConfirm all four wrappers are selected:
command -v dscreate/home/ldapuser/bin/dscreateConfirm the dsctl wrapper is selected:
command -v dsctl/home/ldapuser/bin/dsctlConfirm the dsconf wrapper is selected:
command -v dsconf/home/ldapuser/bin/dsconfConfirm the dsidm wrapper is selected:
command -v dsidm/home/ldapuser/bin/dsidmIf 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:
ls "$HOME/bin"dsconf dscreate dsctl dsidmA wrapper redirects tools into your private root before calling the system binary:
#!/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:
dscreate interactiveThe 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:
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
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-devThe installer reports the on-disk name slapd-ldap-dev. For dsctl commands, use the short name ldap-dev.
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:
dsctl -lslapd-ldap-devConfirm the new instance is running:
dsctl ldap-dev statusInstance "ldap-dev" is runningConfirm ns-slapd runs as your user:
pgrep -a -u "$USER" ns-slapd25110 /usr/sbin/ns-slapd -D /home/ldapuser/dsroot/etc/dirsrv/slapd-ldap-dev -i /home/ldapuser/dsroot/run/dirsrv/slapd-ldap-dev.pidThe -D path points at your custom root, not /etc/dirsrv.
Check listeners on the ports you chose:
ss -lntp | grep -E ':1389|:1636'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:
ldapsearch -x -H ldap://localhost:1389 -D "cn=Directory Manager" -W -b "dc=dev,dc=example,dc=com" -s baseEnter the Directory Manager password when prompted. A successful base search returns the suffix entry:
# 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 SuccessThis 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
ldapsearchexample for password authentication over the network
Non-root instances store everything under your custom Directory Server root:
$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:
dsctl ldap-dev status
dsctl ldap-dev stop
dsctl ldap-dev start
dsctl ldap-dev restartThese 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 startmanually as the owning user - If
dsctlcannot access the instance, confirmcommand -v dsctlreturns$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-listenhostandnsslapd-securelistenhostthrough dsconf, then restart. Do not usensslapd-localhostfor binding—it names the host, not the listen interface. Changecn=configthroughdsconfor LDAP, not by editingdse.ldifon a running server. - Firewall — open TCP
1389and1636when 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:
Error: port 1389 is already in use, or missing NET_BIND_SERVICEFor 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:
ls -ld ~ ~/bin ~/dsrootdrwx------. 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/dsrootRemove the instance and private environment
Removal is destructive. Back up anything you need from $HOME/dsroot first.
- Stop and remove the instance:
dsctl ldap-dev stopRemove the instance data when you are sure nothing else depends on it:
dsctl ldap-dev remove --do-itRemoving instance ...
Non-privileged user cannot use semanage, will not relabel ports or files.
...
Completed instance removalThe semanage warning may repeat during removal; that is expected.
- 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:
rm -rf "$HOME/dsroot" "$HOME/bin"/{dscreate,dsctl,dsconf,dsidm}- 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
- Have an administrator install 389 Directory Server packages system-wide.
- Log in as a regular user and run
dscreate ds-rootto create$HOME/dsrootand administration wrappers in$HOME/bin. - Run
hash -rand confirm all four wrappers resolve under$HOME/bin. - Create the instance with
dscreate interactive(orfrom-file), using LDAP and LDAPS ports at or above1024. - Manage the running
ns-slapdprocess with user-specificdsctl. Do not usesystemctl dirsrv@INSTANCE.
What's Next
- Create an instance with a dscreate INF file — root-owned provisioning for comparison
- Run multiple instances on one host — additional instances with unique ports
- Install 389 Directory Server — package install and first instance baseline
References
- Red Hat Directory Server 13 — Setting up a non-root instance — supported workflow, limitations, ports, and firewall
- dscreate(8) manual —
ds-root, interactive, and INF command syntax - 389 Directory Server 3.2.0 release — tested upstream release information

