passwd is one of the first commands most people learn on Linux, but the bare passwd invocation to change your own password is really just the front door — the command has a full policy-management side that most tutorials skip entirely: expiration, locking, aging rules, status reporting. I want to cover both halves properly, because the administrative side is what actually matters once you’re managing real users on a real system.
What passwd Does
passwd updates a user’s authentication token (their password) and, with the right flags, manages password aging and account lock state. It operates on /etc/shadow (the hashed password store) rather than /etc/passwd directly on any modern system — /etc/passwd itself hasn’t stored actual password hashes since the shadow password system became standard decades ago.
Syntax
passwd [options] [LOGIN]
Confirmed from passwd --help on a current Ubuntu 24.04 system:
Usage: passwd [options] [LOGIN]
Options:
-a, --all report password status on all accounts
-d, --delete delete the password for the named account
-e, --expire force expire the password for the named account
-h, --help display this help message and exit
-k, --keep-tokens change password only if expired
-i, --inactive INACTIVE set password inactive after expiration to INACTIVE
-l, --lock lock the password of the named account
-n, --mindays MIN_DAYS set minimum number of days before password change
-q, --quiet quiet mode
-r, --repository REPOSITORY change password in REPOSITORY repository
-R, --root CHROOT_DIR directory to chroot into
-S, --status report password status on the named account
-u, --unlock unlock the password of the named account
-w, --warndays WARN_DAYS set expiration warning days
-x, --maxdays MAX_DAYS set maximum number of days before password change
Parameters Explained in Depth
| Option | Purpose |
|---|---|
| (no args) | Change your own password interactively |
LOGIN (as root) | Change or manage the password of a specified user |
-S | Report password status: locked/usable, last change date, aging rules |
-l | Lock the account by prefixing the password hash with !, blocking password authentication |
-u | Reverse -l, unlocking the account |
-e | Force immediate expiration — the user must set a new password on next login |
-n MIN | Minimum days that must pass before the password can be changed again |
-x MAX | Maximum days before the password expires and must be changed |
-w WARN | Days before expiration to start warning the user at login |
-i INACTIVE | Days after expiration before the account is fully disabled if no new password is set |
-d | Remove the password entirely (dangerous — allows passwordless login unless other auth is blocked) |
-a | Report status for all accounts (used with -S) |
Tested Output: Status Reporting
$ passwd -S root
root L 2026-04-10 0 99999 7 -1
The fields, in order: username, status (L=locked, P=usable password set, NP=no password), date of last change, min days, max days, warn days, inactive days. This single line tells you everything about an account’s password policy at a glance, which makes passwd -S my go-to for auditing.
Common Administrative Workflows
Setting a password for a new user
useradd -m newuser
passwd newuser
New password:
Retype new password:
passwd: password updated successfully
Forcing a password change on next login
passwd -e newuser
I use this constantly for freshly provisioned accounts — set a known temporary password, then force the user to choose their own on first login rather than continuing to use one an admin knows.
Locking a compromised or departing employee’s account
passwd -l former_employee
This is the fast, immediate first step in an offboarding process — it prevents password-based login instantly, though note it does not kill existing active sessions or invalidate SSH keys, which need separate handling (pkill -u former_employee, revoking authorized_keys, disabling in any SSO/directory system).
Enforcing password aging policy
passwd -n 7 -x 90 -w 14 username
This sets: minimum 7 days between changes (prevents someone from cycling through required changes instantly back to their old password), maximum 90-day validity, and a 14-day advance warning before expiration.
Auditing every account’s status
passwd -Sa
Or, more targeted for scripting:
for user in $(cut -d: -f1 /etc/passwd); do
passwd -S "$user" 2>/dev/null
done
How passwd Works Internally
When you set a password, passwd hashes it using the algorithm configured in /etc/login.defs (ENCRYPT_METHOD, typically SHA512 or increasingly yescrypt on newer distributions) combined with a randomly generated salt, then writes the result into /etc/shadow — never in plaintext, and never in /etc/passwd.
A typical /etc/shadow line:
username:$y$j9T$...(hash)...:19800:7:90:14:::
Fields, colon-separated: username, hashed password, days since epoch of last change, min days, max days, warn days, inactive days, expiration date, reserved field.
/etc/shadow is readable only by root (mode 640, group shadow on most distros) — this separation from the world-readable /etc/passwd is precisely why the shadow password system was introduced: it prevents unprivileged users from even accessing the hashes to run offline cracking attempts.
passwd itself is setuid-root (like su), because writing to /etc/shadow requires root privileges even when a regular user is only changing their own password. Behind that setuid boundary, PAM (/etc/pam.d/passwd) enforces password complexity rules via modules like pam_pwquality, rejecting weak passwords before they’re ever hashed and written.
Password Complexity Enforcement
On most modern distributions, pam_pwquality (successor to the older pam_cracklib) governs what passwd will accept interactively, configured in /etc/security/pwquality.conf:
minlen = 12
dcredit = -1
ucredit = -1
lcredit = -1
ocredit = -1
retry = 3
This enforces a minimum 12-character length with required digit, uppercase, lowercase, and special-character classes. Root bypasses these checks when setting another user’s password (an intentional administrative override), but a regular user changing their own password is subject to them.
Real-World Scripting Example: Bulk Onboarding
#!/bin/bash
set -euo pipefail
while IFS=, read -r username fullname; do
useradd -m -c "$fullname" "$username"
temp_pass=$(openssl rand -base64 12)
echo "${username}:${temp_pass}" | chpasswd
passwd -e "$username"
echo "$username : $temp_pass" >> /root/new_accounts_$(date +%F).txt
done < new_hires.csv
chmod 600 /root/new_accounts_$(date +%F).txt
I use chpasswd rather than piping into passwd directly for bulk operations — it’s designed specifically for non-interactive batch password setting and avoids the prompt-driven interface passwd expects.
Troubleshooting
“passwd: Authentication token manipulation error” — often a full / or /var partition preventing the shadow file from being rewritten (it’s written atomically via a temp file and rename), or /etc/shadow has incorrect permissions/is read-only; check df -h and file permissions first.
User locked out but -S shows P (password set) — the lock may be happening elsewhere: check /etc/passwd for a shell of /sbin/nologin, check for SSH-specific restrictions (AllowUsers/DenyUsers in sshd_config), or PAM account restrictions (pam_time, pam_access) unrelated to the password itself.
Password change accepted by passwd but SSH key login still works — expected: locking a password (passwd -l) does not disable SSH public-key authentication, which is a separate mechanism entirely; for a full account lockdown, also handle authorized_keys and consider usermod -s /sbin/nologin.
“password expired, must change” at login blocking automated processes/cron jobs run as that user** — service accounts should generally have password aging disabled entirely: passwd -x -1 serviceaccount or a matching entry in /etc/login.defs defaults for system accounts.
Related Commands
| Command | Purpose |
|---|---|
passwd | Interactive password changes and per-account policy management |
chpasswd | Non-interactive, batch password setting — reads user:password pairs from stdin |
chage | Dedicated password aging management, a more detailed alternative to passwd -n/-x/-w/-i |
usermod -L / -U | Alternative lock/unlock mechanism, functionally similar to passwd -l/-u |
pwck | Validates the internal consistency of /etc/passwd and /etc/shadow |
openssl passwd | Generates a standalone password hash outside the live user database, useful for pre-seeding /etc/shadow in automated provisioning (cloud-init, Kickstart) |
chage -l username gives a more human-readable aging report than passwd -S if you want dates spelled out rather than the compact single-line format.
Security Implications
- Never set passwords via command-line arguments to any tool in a way that lands in shell history or process listings (
ps auxvisible to other users) —chpasswdreading from stdin, orpasswdinteractively, are the safe patterns. - Lock (
-l), don’t delete (-d), accounts you’re disabling —-dremoves the password field entirely, which under certain misconfigurations can be interpreted as “no password required” rather than “cannot log in,” a dangerous ambiguity best avoided. - Rotate the password hashing algorithm forward when distributions update defaults (
SHA512toyescrypt, for example) — existing hashes aren’t automatically re-hashed, only newly-set passwords get the new algorithm, so a long-lived account’s hash might be using a weaker legacy scheme until its password is next changed. - Enforce aging policy (
-x,-w) organization-wide via/etc/login.defsdefaults for newly created accounts rather than relying on manually applying it per-user, which inevitably gets missed.
Distribution Compatibility
passwd is provided by shadow-utils (RHEL/Fedora naming) or passwd/login packages (Debian/Ubuntu naming) — functionally the same underlying shadow-password toolset across essentially every mainstream distribution, with consistent flag behavior. PAM module availability differs slightly: pam_pwquality is standard on RHEL/Fedora/recent Debian-Ubuntu, while older or minimal systems may still reference the deprecated pam_cracklib. Default hashing algorithm varies by distribution and release — check /etc/login.defs‘s ENCRYPT_METHOD directive rather than assuming.
Password Hashing Algorithm Evolution
It’s worth understanding how the hash format itself has evolved, since it directly affects what you’ll see in /etc/shadow and how secure existing hashes actually are. The leading $id$ prefix on a shadow hash identifies the algorithm used:
| Prefix | Algorithm |
|---|---|
$1$ | MD5 (obsolete, insecure by modern standards) |
$5$ | SHA-256 |
$6$ | SHA-512 (long-standing default on most distributions) |
$y$ | yescrypt (newer default on recent Debian/Ubuntu/Fedora releases, memory-hard and specifically designed to resist GPU-based cracking far better than SHA-512) |
You can check which algorithm is configured as the default for newly-set passwords with:
grep ENCRYPT_METHOD /etc/login.defs
Because passwd only re-hashes a password when it’s actually changed, a long-lived account created years ago may still be carrying an older, weaker hash format even after the distribution’s default has moved on to something stronger — this is a legitimate reason to periodically enforce password rotation on old accounts, beyond the usual security-hygiene arguments for rotation.
Integrating passwd with Centralized Authentication
On systems joined to LDAP, FreeIPA, or Active Directory via SSSD, running plain passwd for a directory-managed account transparently routes the change through PAM to the central directory service rather than the local /etc/shadow file — the command-line interface stays identical, but the actual write target differs entirely depending on how NSS and PAM are configured (/etc/nsswitch.conf and /etc/pam.d/, respectively). This is worth confirming explicitly in mixed environments:
passwd -S someuser
If this reports LDAP or similar rather than expected local shadow-style output, or fails entirely, it’s a strong signal the account is centrally managed and any password policy questions (aging, complexity) need to be addressed on the directory server side, not through local /etc/login.defs tuning, which has no effect on directory-managed accounts.
The Historical Shift to Shadow Passwords
It’s worth briefly understanding why /etc/shadow exists as a separate file at all, since it explains several of passwd‘s permission-related behaviors. In very old Unix and early Linux systems, password hashes lived directly inside /etc/passwd, a file that had to remain world-readable because so many other tools relied on reading it for username/UID lookups. That meant every user’s password hash — even though it was hashed, not plaintext — was available for offline dictionary and brute-force attacks to anyone with any shell access at all. The shadow password suite split the sensitive hash data out into /etc/shadow, restricted to root (and the shadow group in some configurations, for tools like passwd that need read access without full root), while leaving /etc/passwd world-readable for the username/UID metadata everything else still needs. This split is why passwd must be setuid-root even for a user changing only their own password: writing to /etc/shadow requires privileges the calling user doesn’t otherwise have, entirely by design.
Summary
passwd is deceptively deep: the interactive password-change use case everyone learns first is a small fraction of what it actually manages — locking, expiration, aging policy, and status auditing are the parts that matter once you’re responsible for real user accounts on a real system. Understanding its relationship to /etc/shadow, PAM, and the setuid boundary it operates across explains both its behavior and why it deserves careful handling in any provisioning or offboarding automation.
References
- Linux man-pages —
passwd(1): https://man7.org/linux/man-pages/man1/passwd.1.html - Linux man-pages —
shadow(5): https://man7.org/linux/man-pages/man5/shadow.5.html - Linux-PAM —
pam_pwquality: https://man7.org/linux/man-pages/man8/pam_pwquality.8.html - Red Hat Documentation — Configuring Password Policies: https://access.redhat.com/documentation/