chage
When a password expires, and when the account does
chage reads and sets the dates in /etc/shadow: when the password was last changed, how long it
may be kept, how much warning comes before it expires, how long after that the account can still
log in to change it, and a date on which the whole account stops working. chage -l prints those
settings worked out into dates, which is the easiest way to find out why a login is being refused.
Every limit except the account's own expiry is counted from the last change, so setting a maximum age on a password that is already old can expire it immediately. The account's expiry date is fixed, and it is separate from the password: an expired account cannot log in by any route, where an expired or locked password only stops the password itself. Root can change any account's settings, and an ordinary user can read their own.
Sample files used on this page
Every example below was run against these files. Recreate them to follow along.
the accounts The same two accounts as managing-users, id and passwd, rebuilt before every example. Both have a locked password and the package's default ageing, and each one's last password change is pinned to 1 September 2026, so every date chage works out is the same on every run.
tips-dev:x:1001:1001:Deployment account:/home/tips-dev:/bin/bash
tips-ops:x:1002:1002:Operations account:/home/tips-ops:/bin/bash
tips-dev L 2026-09-01 0 99999 7 -1
tips-ops L 2026-09-01 0 99999 7 -1
Reading the settings
Read an account's ageing settings
chage -l tips-dev
The first four lines are dates, worked out from the numbers in the last three. A maximum of 99999 days, about 273 years, is the package default and means the password never expires.
Show output
Last password change : Sep 01, 2026
Password expires : never
Password inactive : never
Account expires : never
Minimum number of days between password change : 0
Maximum number of days between password change : 99999
Number of days of warning before password expires : 7
Print the dates in ISO form
chage -l -i tips-dev | head -3
-i prints YYYY-MM-DD, which sorts correctly and reads the same in every locale, so it is the form to use when a script is going to compare it.
Show output
Last password change : 2026-09-01
Password expires : never
Password inactive : never
Read where the settings are stored
chage -M 90 -I 14 -E 2027-03-31 tips-dev; getent shadow tips-dev | cut -d: -f3-8
Fields 3 to 8 of the account's line in /etc/shadow: the last change, the minimum and maximum age, the warning, the inactivity period and the account's expiry. The two dates are days since 1 January 1970, so 20697 is 1 September 2026. An empty field means no limit.
Show output
20697:0:90:7:14:20908
Read your own settings as an ordinary user
runuser -u tips-dev -- chage -l tips-dev | head -2
chage -l works for the account running it, so anyone can find out when their own password expires. Changing the settings needs root.
Show output
Last password change : Sep 01, 2026
Password expires : never
Try to read another account's settings
runuser -u tips-dev -- chage -l tips-ops
Refused, with no details about why. The dates are in /etc/shadow, which only root and the shadow group can read.
Show output
chage: Permission denied.
Expiring passwords
Each setting below is counted in days from the last password change, which here is 1 September 2026.
Make passwords expire after 90 days
chage -M 90 tips-dev; chage -l tips-dev | head -2
-M is the maximum age. Ninety days from 1 September is 30 November, and after that the next login has to choose a new password before it gets a shell.
Show output
Last password change : Sep 01, 2026
Password expires : Nov 30, 2026
Warn people before their password expires
chage -M 90 -W 14 tips-dev; chage -l tips-dev | tail -1
-W is how many days before expiry a login starts printing a warning. Seven is the default.
Show output
Number of days of warning before password expires : 14
Disable an account left unchanged after expiry
chage -M 90 -I 14 tips-dev; chage -l tips-dev | sed -n 2,3p
-I is the inactivity period. Up to 14 days after the password expires, a login can still change it. After that the account is disabled, and only root can get it working again.
Show output
Password expires : Nov 30, 2026
Password inactive : Dec 14, 2026
Stop a password being changed straight back
chage -m 7 tips-dev; chage -l tips-dev | grep Minimum
-m is the minimum age: once a password has been changed, its owner cannot change it again for that many days. With a password history policy, it stops someone changing their password several times in a minute to get back to the old one. Root is not held to it.
Show output
Minimum number of days between password change : 7
Force a new password at the next login
chage -d 0 tips-dev; chage -l tips-dev | head -3
-d sets the date of the last change, and 0 is 1 January 1970, so the password counts as expired. It is the same as passwd -e, and the usual step after setting a temporary password for someone.
Show output
Last password change : password must be changed
Password expires : password must be changed
Password inactive : password must be changed
Record a password change made somewhere else
chage -d 2026-09-15 tips-dev; chage -l tips-dev | head -1
-d with a date sets the last change to that day, without touching the password itself. Useful when accounts are copied between machines and their ageing would otherwise restart from the day of the copy.
Show output
Last password change : Sep 15, 2026
Expiring the account
An account's expiry date is a fixed day, unrelated to when the password was changed. Once it has passed, the account cannot log in by any route, a key or a password, which is what makes it the right control for a temporary account.
Expire the account on a particular date
chage -E 2027-03-31 tips-dev; chage -l tips-dev | grep 'Account expires'
The account works until the end of 30 March and stops on the 31st. passwd shows an expired account being refused by SSH even with a working key.
Show output
Account expires : Mar 31, 2027
Give the date as a number of days
chage -E 20818 tips-dev; chage -l tips-dev | grep 'Account expires'
-E and -d both take a number of days since 1 January 1970 as well as a date, which is the form /etc/shadow stores and the form a script working out a date with arithmetic will have.
Show output
Account expires : Dec 31, 2026
Remove an account's expiry date
chage -E 2027-03-31 tips-dev; chage -E -1 tips-dev; chage -l tips-dev | grep 'Account expires'
-1 clears the field, so the account never expires. -I -1 clears the inactivity period the same way.
Show output
Account expires : never
Beside passwd -l
Lock a password without changing its ageing
chage -M 90 tips-dev; passwd -l tips-dev; chage -l tips-dev | sed -n 2p
A lock and an expiry are separate. passwd -l only puts a ! in front of the stored password, and leaves every date where it was, so unlocking the password later finds the same expiry still waiting.
Show output
passwd: password changed.
Password expires : Nov 30, 2026