Python pwd: Query Unix User Accounts

Published on: August 26, 2026
Reading time: 5 minutes
Close-up of HTML and CSS code displayed on a computer screen, ideal for tech and programming themes.

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).

Share:

Facebook
WhatsApp
Twitter
LinkedIn

Article content

    Related articles

    Python asynchronous code on a laptop for inspect.markcoroutinefunction
    Advanced Python
    Foto de perfil de Leandro Hirt da Academify

    markcoroutinefunction: Detect Async Wrappers

    Learn inspect.markcoroutinefunction in Python to identify async wrappers, integrate frameworks, and avoid incorrect coroutine detection.

    Ler mais

    Tempo de leitura: 5 minutos
    10/10/2026
    Python code for traversing folders and files with Path.walk
    Advanced Python
    Foto de perfil de Leandro Hirt da Academify

    Path.walk: Traverse Directories Safely

    Learn Python Path.walk to traverse directories, filter files, handle errors, and control directory-tree processing safely.

    Ler mais

    Tempo de leitura: 5 minutos
    10/10/2026
    Python process debugging in a terminal with source code
    Advanced Python
    Foto de perfil de Leandro Hirt da Academify

    pdb -p: Debug Running Python Processes

    Learn how to attach pdb to a running Python process, inspect stacks, and diagnose hangs safely.

    Ler mais

    Tempo de leitura: 6 minutos
    09/10/2026
    Python programming code for exact fractions
    Advanced Python
    Foto de perfil de Leandro Hirt da Academify

    fractions.from_number: Convert Numbers to Fractions

    Learn Python fractions.from_number for exact rational conversion, float and Decimal handling, approximation, validation, and safe arithmetic.

    Ler mais

    Tempo de leitura: 4 minutos
    09/10/2026
    Developer configuring a Python HTTPS server and TLS certificate
    Advanced Python
    Foto de perfil de Leandro Hirt da Academify

    HTTPSServer: Build a Local HTTPS Server in Python

    Learn Python HTTPSServer for local HTTPS services, TLS certificates, threaded handling, testing, and practical security limits.

    Ler mais

    Tempo de leitura: 4 minutos
    08/10/2026
    Protected files representing secure TAR extraction with Python
    Advanced Python
    Foto de perfil de Leandro Hirt da Academify

    tarfile extraction_filter: Extract TAR Safely

    Learn Python tarfile extraction_filter for safer TAR extraction, path validation, links, permissions, and resource limits.

    Ler mais

    Tempo de leitura: 5 minutos
    08/10/2026