time getent passwd deploy returning in 40 ms looks harmless until you multiply it by a hundred thousand per hour. On a VPS that authenticates against LDAP or Active Directory, every getpwnam(), getgrnam() and group membership check can become a network round trip. The classic pathology is a web request that feels 300 ms slower than it should, because nginx or a PHP-FPM worker triggered an NSS lookup for a supplementary group over the network. The fix is almost always caching, and on modern systems that means nsswitch.conf with sss in front and SSSD’s memcache_timeout and enumeration controls tuned.
This article walks through measuring the lookup cost, configuring SSSD so that a normal lookup never blocks on the network, and verifying the cache is actually being hit. It assumes Rocky, Alma, Ubuntu or Debian with SSSD installed and realmd or a hand-written sssd.conf binding to an LDAP directory.
Measuring the Baseline Lookup Cost
Start with the tool the C library actually uses. getent resolves through every NSS module in order, so its runtime is the true user-visible cost. Run it repeatedly with the cache cold and warm, and compare with a direct network query.
for i in $(seq 1 5); do
/usr/bin/time -f '%e s' getent passwd deploy 2>&1 | tail -1
done
for i in $(seq 1 5); do
/usr/bin/time -f '%e s' getent group wheel 2>&1 | tail -1
done
# direct LDAP round trip for comparison
ldapsearch -H ldaps://dc1.example.com -x -b 'dc=example,dc=com' \
'(uid=deploy)' uid 2>/dev/null | grep -c '^uid:'
If getent is steadily 20–60 ms and ldapsearch shows the same, you are paying a full network round trip per lookup. Worse, if the first getent takes 5–12 seconds, you are hitting a resolver timeout or an unreachable server being retried. Both are SSSD configuration problems rather than application bugs.
Confirm which module answers the query with getent -s sss passwd deploy versus getent -s files passwd root. If the first is slow and the second instant, the cost is entirely in the directory path and caching is the lever.
Ordering nsswitch Correctly
Order matters because NSS stops at the first module that returns a hit. files first is mandatory for local service accounts; sss should follow and ldap should generally be absent if SSSD is managing the connection, because a direct ldap module bypasses the cache entirely.
# /etc/nsswitch.conf
passwd: files sss systemd
group: files sss systemd
shadow: files sss
hosts: files dns myhostname
sudoers: files sss
services: files sss
Removing ldap from passwd and group is a common rescue for slow logins. The direct module has no negative cache, so a miss for a non-existent user retries the server each time. SSSD, by contrast, records negative results and answers from the cache. Run getent passwd after the change and compare time-to-first-line; a large drop usually means the direct module was taking hits.
SSSD Cache Priming and Timers
SSSD’s NSS responder serves from a local LDB database, refreshed in the background. Three options matter: memcache_timeout controls in-memory caching of results, entry_cache_timeout controls how long a user record is trusted on disk, and refresh_expired_interval controls proactive refresh.
# /etc/sssd/sssd.conf
[domain/example.com]
id_provider = ldap
auth_provider = ldap
ldap_uri = ldaps://dc1.example.com
cache_credentials = true
entry_cache_user_timeout = 3600
entry_cache_group_timeout = 3600
entry_cache_netgroup_timeout = 3600
refresh_expired_interval = 300
[nss]
memcache_timeout = 600
enumerate = false
filter_users = root, nobody
filter_groups = root, nobody
enumerate = false is critical on a VPS. When enabled, SSSD lists every user and group in the directory at startup and periodically, which turns a small directory into a sustained CPU and network load and makes getent passwd with no argument crawl. Most applications never need it. filter_users prevents lookups for local accounts from ever reaching the directory.
A one-hour entry_cache_user_timeout raises the cache hit ratio dramatically while keeping the worst-case staleness bounded. If group membership changes must propagate quickly, keep entry_cache_group_timeout lower—300 to 600 seconds—rather than lowering every timeout. Restart SSSD after edits and confirm it parsed: sssd --genconf plus journalctl -u sssd -n 50 --no-pager.
Verifying Cache Hits
Configuration without verification is guesswork. SSSD logs at a debug level you control per service, and the NSS responder logs each request with its source and outcome. Raise the level temporarily:
# temporarily
sssctl debug-level 8
systemctl restart sssd
getent passwd deploy >/dev/null
getent passwd deploy >/dev/null
tail -50 /var/log/sssd/sssd_nss.log
sssctl debug-level 0 # back to default
The first request should show an LDAP fetch, and the second an immediate return from the cache with no network operation. sssctl cache-expire -u deploy forces a refresh if you want to test the cache-miss path deliberately. Use sssctl domain-status example.com to confirm the domain is online and sssctl user-checks deploy to inspect the resolved records.
Handling Directory Outages Gracefully
On a VPS in a different network zone from the directory, brief outages are common. SSSD’s offline_timeout and cache_credentials determine whether authentication still works when the server is unreachable. With cache_credentials = true and a warm cache, users who logged in recently can authenticate offline; the default krb5_store_password_if_offline behaviour is relevant for Kerberos-backed domains.
Also set ldap_network_timeout and ldap_opt_timeout to short values, typically 3–6 seconds, so a silent server does not cause a 30-second stall on first lookup. Pair that with dns_resolver_timeout in the domain section and, if you have multiple DCs, list them all in ldap_uri so failover is local to SSSD rather than the application.
What the Improvement Looks Like
On a representative 2 vCPU instance with a 45 ms round trip to the directory, moving from direct ldap in nsswitch to sss with a 600-second memcache typically takes steady-state getent from 45–60 ms to under 1 ms, and removes the 5-second tails caused by resolver retries. The measurable proxy is a login loop or a request path that performs authorization:
# before/after: 200 lookups, report total time
/usr/bin/time -f 'total %e s' bash -c 'for i in $(seq 1 200); do getent passwd deploy >/dev/null; done'
# group membership lookups are the usual hidden cost
/usr/bin/time -f 'total %e s' bash -c 'for i in $(seq 1 200); do id deploy >/dev/null; done'
If the total barely moves, the cache is not being used—check that sss actually precedes any other network module and that memcache_timeout is non-zero. On small instances, the CPU saved by avoiding per-request directory queries is often more valuable than the latency win.
Directory-backed authentication is one of the few places where a modest instance can feel fast or slow based purely on configuration, regardless of CPU or memory. If you are choosing a platform for this kind of workload, our VPS vs dedicated server comparison covers the latency and isolation trade-offs. For teams that would rather not own the SSSD tuning and directory integration themselves, it is worth understanding what falls inside and outside a support boundary—compare managed vs unmanaged in our guide.



Leave a Reply
You must be logged in to post a comment.