Problem
On affected Univention Corporate Server (UCS) systems, recurring warning messages appear in /var/log/univention/listener.log , potentially triggering alerts in monitoring systems.
Every hour, when the Primary Directory Node updates the LDAP license cache, the resulting license-object changes are replicated through the Backup Directory Node to the Replica Directory Node.
During the first replication transaction after a period of inactivity, the Univention Directory Listener attempts to reuse an LDAP connection that has already been closed by OpenLDAP (slapd ) due to the configured idle timeout of 360 seconds.
The stale LDAP connection causes the first LDAP operation to fail with the following error:
ldap.SERVER_DOWN: Can't contact LDAP server
additional info: Broken pipe
The following messages can be observed in /var/log/univention/listener.log :
2026-08-31T08:00:02.112000+02:00 PROCESS connecting to ldap://primary.uni-standard.de:7389
2026-08-31T08:00:02.150740+02:00 PROCESS updating 'cn=admin,cn=license,cn=univention,dc=uni-standard,dc=de' command m
2026-08-31T08:00:02.152111+02:00 PROCESS connecting to ldap://localhost:7389
2026-08-31T08:00:02.189382+02:00 WARNING replication: Can't contact LDAP server: retrying
2026-08-31T08:00:02.189444+02:00 WARNING additional info: Broken pipe
Although the warning indicates a failed LDAP operation, the error is transient. The replication module automatically reconnects to the LDAP server and retries the operation.
The retry succeeds, and the pending LDAP modification is processed successfully.
However, because the initial connection failure is logged at the WARNING level, monitoring systems that evaluate the Directory Listener log may generate recurring alerts, even though LDAP replication eventually completes successfully.
Root Cause
The issue is tracked in the following Univention Bugzilla report:
Bug: Bug 59964
The root cause is the interaction between the configured LDAP idle timeout and the persistent LDAP connection maintained by the Univention Directory Replication module.
The following sequence leads to the warning:
-
The replication module establishes an LDAP connection to the local LDAP server.
-
The LDAP connection remains unused for an extended period.
-
OpenLDAP (
slapd) closes the connection after 360 seconds of inactivity, according to its configuredidletimeout. -
The replication module retains the corresponding LDAP handle and does not verify whether the underlying TCP socket is still valid before reusing it.
-
During the next hourly license cache update, a new LDAP modification is replicated through the domain.
-
The replication module attempts to perform an LDAP operation using the previously closed connection.
-
The operation fails with
ldap.SERVER_DOWNand the additional error informationBroken pipe. -
The built-in retry mechanism establishes a new LDAP connection and successfully processes the pending modification.
The underlying issue is that the replication module retains a persistent LDAP handle without performing a connection health check before reuse.
Consequently, the Python LDAP object can remain available even though the associated TCP connection has already been terminated by the LDAP server.
Important: The license cache update does not directly close the LDAP connection. Instead, the hourly update acts as a predictable trigger that causes the replication module to reuse the stale LDAP handle.
The subsequent reconnection and retry are successful, which is why the observed warning does not necessarily indicate an actual replication failure.
Workaround
A temporary workaround is available to prevent the recurring LDAP connection warnings from triggering monitoring alerts.
The workaround modifies the logging behavior of the Univention Directory Replication module. Specifically, it changes the log severity for the retry message from WARNING to INFO .
This prevents the corresponding retry messages from appearing as warnings in the Directory Listener log and reduces unnecessary monitoring alerts.
Important considerations:
-
This modification is a workaround, not a permanent solution to the underlying LDAP connection handling issue.
-
The patch does not prevent stale LDAP connections or change the existing reconnection and retry behavior.
-
The modification only changes the log severity of the affected LDAP retry message from
WARNINGtoINFO. -
The change is applied directly to a Python module installed by a UCS package.
-
A future package update may overwrite the modified file and remove the workaround.
-
The modification is therefore not update-safe and may need to be reapplied after updating the relevant package.
-
Because the patch changes the logging severity for this retry path, other occurrences of
ldap.SERVER_DOWNhandled by the same code may also be logged atINFOrather thanWARNING.
The following procedure applies the workaround.
1. Install the required patch utility
Ensure that the patch package is installed on the affected system.
Update the package index and install the required package:
apt update && apt install patch
2. Back up the original replication module
Before modifying the replication module, create a backup of the original Python file.
Run:
cp /usr/lib/univention-directory-listener/system/replication.py /usr/lib/univention-directory-listener/system/replication.py.bak
The backup is stored at:
/usr/lib/univention-directory-listener/system/replication.py.bak
This backup allows the original file to be restored if necessary.
3. Create the patch file
Create a patch named patch-replication-retry-warning-to-info.patch .
Execute the following command:
cat > patch-replication-retry-warning-to-info.patch <<'EOF'
diff --git usr/lib/univention-directory-listener/system/replication.py usr/lib/univention-directory-listener/system/replication.py
--- usr/lib/univention-directory-listener/system/replication.py
+++ usr/lib/univention-directory-listener/system/replication.py
@@ -563,7 +563,7 @@ def handler(dn: str, new: dict[str, list[bytes]], listener_old: dict[str, list[bytes]], operation: str) -> Any:
ud.debug(ud.LISTENER, ud.ALL, 'replication: delete: %s' % dn)
_delete_dn_recursive(lo, dn)
except ldap.SERVER_DOWN as ex:
- log_ldap(ud.WARN, 'retrying', ex)
+ log_ldap(ud.INFO, 'retrying', ex)
reconnect = True
handler(dn, new, listener_old, operation)
except ldap.ALREADY_EXISTS as ex:
EOF
The patch changes the following line:
log_ldap(ud.WARN, 'retrying', ex)
To:
log_ldap(ud.INFO, 'retrying', ex)
The retry logic itself remains unchanged:
reconnect = Truehandler(dn, new, listener_old, operation)
As a result, the replication module continues to reconnect and retry LDAP operations when encountering ldap.SERVER_DOWN .
Only the severity of the corresponding log message is modified.
4. Apply the patch
Apply the patch to the installed replication module:
patch /usr/lib/univention-directory-listener/system/replication.py patch-replication-retry-warning-to-info.patch
The patch is designed for the specific source-code structure shown above. If the installed replication module differs from that version, the patch may fail to apply and must be reviewed before proceeding.
After the patch has been applied successfully, the affected retry messages will use the INFO logging level instead of WARNING when the modified module code is executed.
The existing retry mechanism remains operational.
Note: The workaround does not eliminate the initial LDAP connection failure. It only reduces the logging severity of the corresponding retry messages. Monitoring systems should continue to detect genuine LDAP connectivity problems and persistent replication failures.
Investigation
The investigation confirmed that the observed LDAP connection failure is transient.
When the replication module encounters the stale LDAP connection, its built-in retry mechanism establishes a new connection and successfully processes the pending LDAP modification.
The issue occurs regularly when the hourly license cache update is replicated through the UCS domain.
The observed replication path is:
Primary → Backup → Replica
Affected Components
The following components are involved in the observed behavior:
-
Univention Directory Listener
-
Univention Directory Replication
-
OpenLDAP /
slapd -
License cache update
-
LDAP replication via Notifier/Translog
Observed Behavior
The following example illustrates the recurring warning messages recorded in the Directory Listener log.
File:
/var/log/univention/listener.log
Example:
2026-09-20T15:00:02.136601+02:00
PROCESS connecting to ldap://ucs5primary.deadpool.intranet:7389
PROCESS updating 'cn=admin,cn=license,cn=univention,dc=deadpool,dc=intranet' command m
PROCESS connecting to ldap://localhost:7389
WARNING replication: Can't contact LDAP server: retrying
WARNING additional info: Broken pipe
The same pattern repeats approximately every hour.
The warning occurs when the replication module attempts to reuse an LDAP connection that was closed during the preceding idle period.
After the warning, the LDAP connection is recreated, and the pending replication operation completes successfully.
Therefore, the warning indicates a temporary LDAP connection failure rather than a persistent replication problem in the investigated environment.
Trigger: Hourly License Cache Update
The Primary Directory Node executes the following cron job:
0 * * * * root /usr/share/univention-directory-manager-tools/univention-update-license-cache
The corresponding repository source is:
management/univention-directory-manager-modules/scripts/univention-update-license-cache
The cron job runs hourly and is intended for the Primary Directory Node.
There is no active equivalent cron entry on the Replica Directory Node in the investigated environment.
The script establishes an LDAP administrator connection to the configured ldap/master and invokes the license cache update.
This operation generates LDAP modifications that are subsequently distributed through the UCS replication infrastructure.
Because the job executes once per hour, it provides a predictable trigger for the observed connection reuse problem.
The 360-second LDAP idle timeout is significantly shorter than the one-hour interval between license cache updates.
Consequently, an LDAP connection that remains unused between updates may already have been closed by slapd when the next update is processed.
LDAP Changes Performed by the License Cache
The license cache update functionality is implemented in the following repository file:
management/univention-directory-manager-modules/modules/univention/admin/license.py
The update_cache() function performs LDAP searches to determine the number of users, servers, and managed clients.
It subsequently updates up to three LDAP attributes individually:
univentionUsedLicenseUsers
univentionUsedLicenseServers
univentionUsedLicenseManagedClients
The target LDAP object is:
cn=admin,cn=license,cn=univention,<ldap/base>
Each attribute update is performed as a separate LDAP Modify operation.
Consequently, the hourly license cache update can generate one or more LDAP/Notifier transactions that are replicated through the UCS domain.
These transactions are responsible for triggering the replication module after its LDAP connection has potentially remained idle for an extended period.
The license cache update itself is not responsible for terminating the LDAP connection.
Instead, it initiates the next LDAP operation, exposing the stale connection retained by the replication module.
Connection Handling in the Replication Module
The replication module is implemented in:
management/univention-directory-replication/replication.py
The module maintains a global LDAP connection using the following variables:
reconnect: bool = Falseconnection: ldap.ldapobject | None = None
The connect() function reuses an existing LDAP connection when the connection object exists and reconnection has not been requested.
The relevant logic is:
if connection and not reconnect: return connection
There is no connection health check before the existing LDAP handle is reused.
This allows the following state to occur:
Python LDAP handle still exists
TCP socket has already been closed by slapd
Although the Python LDAP connection object remains available, the underlying TCP socket may no longer be usable.
When the next LDAP operation is performed, the client attempts to communicate through the already closed socket.
The operating system returns EPIPE , which indicates a broken pipe.
The python-ldap library reports this condition as:
ldap.SERVER_DOWN
additional info: Broken pipe
The replication module catches the ldap.SERVER_DOWN exception and invokes its reconnection and retry mechanism.
In the affected implementation, the corresponding exception handler contains:
except ldap.SERVER_DOWN as ex: log_ldap(ud.WARN, 'retrying', ex) reconnect = True handler(dn, new, listener_old, operation)
The call to log_ldap() records the exception using the WARNING severity.
The reconnect flag is then set to True , ensuring that the subsequent invocation of the handler establishes a new LDAP connection rather than reusing the stale one.
The pending LDAP modification is then processed again.
Because the reconnection succeeds in the investigated environment, the replication operation completes successfully.
However, the warning messages remain visible in the Directory Listener log and may trigger monitoring alerts.
The workaround described above changes the severity of this retry message to INFO without modifying the connection handling or retry behavior.
Refrences
- Bug report: Univention Bugzilla – Bug 59964