The pwd module queries the Unix user-account database. It can find a login by numeric UID, return UID and primary GID, and expose the configured home directory, login shell, and descriptive account field.
Despite the historical name “password database,” this is not an authentication API. Modern Unix systems normally store credentials in shadow files, PAM, LDAP, or another identity provider. The pw_passwd field usually contains only x, *, or a similar marker.
Availability
pwd is available on Unix but not WASI or iOS. Windows uses a different account model.
try:
import pwd
except ImportError:
pwd = None
For cross-platform software, use higher-level APIs when you only need the current home directory or user display name.
Entry fields
Functions return a tuple-like object with these attributes:
pw_name, pw_passwd, pw_uid, pw_gid, pw_gecos, pw_dir, pw_shell
pw_uid and pw_gid are integers; the other values are strings.
Query by UID
import os
import pwd
account = pwd.getpwuid(os.getuid())
print(account.pw_name)
print(account.pw_dir)
print(account.pw_shell)
getpwuid() raises KeyError when no entry exists.
Query by name
import pwd
try:
account = pwd.getpwnam("deploy")
except KeyError:
print("Unknown user")
else:
print(account.pw_uid, account.pw_gid)
Convert this exception into a clear domain error when exposing a public API.
Real and effective users
os.getuid() returns the real UID, while os.geteuid() returns the effective UID. They can differ in setuid programs, containers, or code that changes privileges.
real_account = pwd.getpwuid(os.getuid())
effective_account = pwd.getpwuid(os.geteuid())
Permission checks must use the identity that the kernel applies to the operation.
Do not trust environment variables
USER, LOGNAME, and HOME can be missing or manipulated.
account = pwd.getpwuid(os.geteuid())
home = account.pw_dir
Under sudo, the effective user may be root while the original caller appears in SUDO_UID. Treat that as an explicit policy decision rather than a silent heuristic.
Home directories
pw_dir is the home path registered by the identity system. It may not exist, be mounted, or be accessible.
from pathlib import Path
home = Path(account.pw_dir)
if home.is_dir():
print(home)
Do not create files in another account’s home without verifying ownership and permissions.
Login shells
pw_shell may contain /bin/bash, /bin/zsh, /usr/sbin/nologin, /bin/false, or another program.
This value does not prove that the account can authenticate or authorize executing that shell. Service accounts may have empty or platform-specific values.
GECOS
pw_gecos often contains a full name or administrative comment, but its format is not a stable application protocol. It may contain comma-separated values and personal data.
Do not use it as a unique identifier and minimize exposure for privacy.
pw_passwd is not authentication
On shadow-password systems, pw_passwd normally contains x or *. Even if a hash appears, comparing it manually is not a safe authentication design.
Use PAM, an enterprise identity service, OAuth, SSH, or the system’s supported mechanism. Never collect a password and try to validate it with pwd.
List accounts
import pwd
for account in pwd.getpwall():
print(account.pw_uid, account.pw_name, account.pw_shell)
The order is arbitrary. On systems connected to LDAP, NIS, SSSD, or another NSS provider, this can be slow and return many accounts.
NSS and remote providers
The module follows the system’s Name Service Switch configuration. Data may come from /etc/passwd, LDAP, SSSD, NIS, container files, or plugins.
A lookup that appears local may perform network I/O and block. Avoid repeated calls in a hot path without an appropriate cache and timeout architecture.
Caching
A small cache can reduce repeated lookups.
from functools import lru_cache
import pwd
@lru_cache(maxsize=256)
def user_by_uid(uid):
return pwd.getpwuid(uid)
lru_cache does not expire. Long-running services may miss account changes, so use TTL caching when freshness matters.
Display file ownership
Use pwd to translate an inode’s UID to a name.
import os
import pwd
info = os.stat("file.txt")
try:
owner = pwd.getpwuid(info.st_uid).pw_name
except KeyError:
owner = str(info.st_uid)
Keep the numeric UID as a fallback. Files can outlive accounts or come from another namespace.
Containers and namespaces
A container process may run with a UID that has no entry in /etc/passwd. This is common in minimal images and platforms that assign random UIDs.
Do not make missing names fatal when a numeric identity is sufficient.
UID zero
UID 0 normally represents root, but checking the string name root is the wrong privilege test.
if os.geteuid() == 0:
print("Effective UID is zero")
Even UID zero may be constrained by namespaces, capabilities, SELinux, AppArmor, or a container.
Dropping privileges
A service started as root may resolve a target account and then reduce privileges.
import os
import pwd
account = pwd.getpwnam("appuser")
os.initgroups(account.pw_name, account.pw_gid)
os.setgid(account.pw_gid)
os.setuid(account.pw_uid)
This sequence is security-sensitive. Prepare directories and descriptors first, never try to regain privileges, and prefer having the service manager start the process with the correct account.
Supplementary groups
pw_gid is only the primary group. Use grp, os.getgroups(), or os.getgrouplist() for supplementary memberships.
Tilde expansion
os.path.expanduser("~name") may consult the same user database. For explicit error handling, use pwd.getpwnam(name).pw_dir.
Validate user names
Do not build paths by concatenating an untrusted login. Resolve the account, use its registered home, and still verify that the final path is within an allowed root.
Privacy
getpwall() may reveal account names, homes, and shells. Do not expose the full database through a web API or logs without a justified need and authorization.
Latency and concurrency
NSS implementations can read configuration files or contact remote services. Do not assume constant low latency merely because the API is synchronous and simple.
Error handling
KeyError means no entry was found. Provider outages and system failures may surface differently. Distinguish a missing account from an unavailable identity service when that difference matters.
Testing
Test existing and missing accounts, numeric UIDs without names, random container UIDs, slow LDAP, nonexistent homes, nologin shells, deleted accounts, root inside a namespace, stale caches, and unsupported platforms.
Unit tests should not depend on the host’s actual accounts. Wrap lookups and provide fake records.
Common mistakes
Common failures include using pw_passwd for authentication, trusting $USER, treating pw_gid as every group, assuming the home exists, listing all accounts on every request, failing when a UID has no name, and exposing the account database unnecessarily.
Conclusion
pwd is the standard interface for resolving Unix identities by UID or login. Use it for names, homes, shells, and ownership while retaining numeric fallbacks and considering NSS, containers, latency, and privacy.
Do not use it to validate passwords or confuse account existence with authorization. Consult the official pwd documentation and passwd(5).







