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

    Stack of cut logs covered in snow, showcasing a cold winter texture and woody elements.
    Advanced Python
    Foto de perfil de Leandro Hirt da Academify

    Python syslog: Send Logs to Unix

    Learn Python syslog to send Unix logs with priorities, facilities, masks, structured content, and protection against log injection.

    Ler mais

    Tempo de leitura: 5 minutos
    26/08/2026
    High-resolution close-up of a CPU processor, RAM sticks, and a hard drive, showcasing modern computer hardware.
    Advanced Python
    Foto de perfil de Leandro Hirt da Academify

    Python resource: CPU and Memory Limits

    Learn Python resource to measure CPU, peak memory, page faults, and set limits for files, processes, descriptors, and address space

    Ler mais

    Tempo de leitura: 5 minutos
    26/08/2026
    A developer typing code on a laptop with a Python book beside in an office.
    Advanced Python
    Foto de perfil de Leandro Hirt da Academify

    Python pty: Automate Unix Terminals

    Learn Python pty to run and test interactive programs, control pseudo-terminals, handle EOF, resize, signals, timeouts, and cleanup.

    Ler mais

    Tempo de leitura: 5 minutos
    25/08/2026
    Close-up of hands typing on a laptop keyboard, Python book in sight, coding in progress.
    Advanced Python
    Foto de perfil de Leandro Hirt da Academify

    Python tty: Raw and Cbreak Modes

    Learn Python tty to use raw and cbreak modes, read keys, parse sequences, handle Unicode, and safely restore Unix terminals.

    Ler mais

    Tempo de leitura: 5 minutos
    25/08/2026
    Detailed view of programming code in a dark theme on a computer screen.
    Advanced Python
    Foto de perfil de Leandro Hirt da Academify

    Python termios: Safe Terminal Control

    Learn Python termios for canonical mode, echo, key reads, baud rate, queues, window size, and safe restoration of POSIX terminals.

    Ler mais

    Tempo de leitura: 5 minutos
    25/08/2026
    Side view of contemplating female assistant in casual style standing near shelves and choosing file with documents
    Advanced Python
    Foto de perfil de Leandro Hirt da Academify

    Python fcntl: File Locks and Control

    Learn Python fcntl for file locks, descriptor flags, ioctl, pipes, and Unix control while avoiding invalid buffers and memory corruption.

    Ler mais

    Tempo de leitura: 5 minutos
    25/08/2026