Block Logins Without Locking an Account with nologin
nologin hides two very different jobs behind one name: a shell that refuses one account, and a file that locks out everyone else. Mix them up and you either fail to lock down a service account or accidentally shut out your whole user base. You will finish with either a service account that refuses interactive logins, or a temporary system-wide login notice you can remove cleanly.
The route
Jump straight to the step you need, or tick off Done means at the end.
Allow about 10 minutes for an account-shell change and its checks. Allow longer for a system-wide lockout, because you must plan an administrative escape route first. You need a root shell, or sudo access, for the changes in this guide; commands that only inspect files or run nologin as a test do not need elevation.
Before you start: choose the scope
- One account: use
/usr/sbin/nologin, normally for a service or technical account that must not start a shell. - Everyone, temporarily: use
/etc/nologinfor a short maintenance window when unprivileged users must be refused byloginand shown a message.
Neither mechanism is a substitute for investigating a compromised account. A disabled login shell does not revoke existing sessions, terminate processes, remove keys, or change a password. Likewise, the global file does not stop every access path on the machine; check the authentication and service configuration that actually applies to your system.
1. Confirm the local implementation
Read the installed documentation before copying an example. On this machine, the command comes from util-linux 2.41.3, while the installed section 8 manual credits shadow-utils 4.13 as its source. That packaging detail matters when comparing behaviour with another distribution:
command -v nologin
nologin --version
man 5 nologin
man 8 nologin
Typical output from the version check is:
/usr/sbin/nologin
nologin from util-linux 2.41.3
The command itself prints a refusal and exits non-zero. Check that directly without touching any account:
/usr/sbin/nologin
printf 'exit status: %s\n' "$?"
Expect This account is currently not available. and exit status 1. That non-zero result is the whole point: do not read it as a failed installation.
2. Give one account a disabled shell
Identify the exact account first. Replace svc_example with a real account name, then inspect its current shell:
getent passwd svc_example
Only change the shell once you have confirmed the returned record is the right one. This is a security-sensitive change: a typo can hit the wrong identity, and a service may depend on a shell for a maintenance hook even without offering interactive access.
Set the shell with usermod as root:
sudo usermod --shell /usr/sbin/nologin svc_example
Verify through the account database rather than trusting that the command touched the right file:
getent passwd svc_example
getent passwd svc_example | awk -F: '{print "login shell: " $7}'
The final field should read /usr/sbin/nologin. A new login attempt that reaches this shell prints the refusal and returns non-zero. Existing sessions stay active, so inspect and end them separately if that is part of your plan.
3. Undo the account change
Record the original shell before you change it. If you did not, read it back from a trusted account database record or your account-management documentation. For a normal Bash account, the undo command might be:
sudo usermod --shell /bin/bash svc_example
getent passwd svc_example | awk -F: '{print "login shell: " $7}'
Do not blindly substitute /bin/bash for a service account: restore whatever shell was actually intended, such as /bin/sh or an application-specific wrapper. If the account comes from LDAP, SSSD or another directory source, the authoritative shell may not even live in local /etc/passwd.
4. Announce a temporary system-wide lockout
Before creating /etc/nologin, open a root-capable session and keep it open until maintenance ends. The section 5 manual says that when this readable file exists, login allows access only to root, shows its contents to everyone else, and refuses their logins. This is deliberately disruptive.
Write a short, useful message as root. The redirection needs elevation, so use sudo tee rather than putting sudo in front of a plain shell redirection:
printf '%s\n' 'System maintenance is in progress. Please try again after 23:00 UTC.' \
| sudo tee /etc/nologin >/dev/null
sudo chmod 0644 /etc/nologin
sudo cat /etc/nologin
Expect the same maintenance sentence back. Check the file is readable before relying on it:
test -r /etc/nologin && echo 'system login gate is active'
Warning
This file is not a universal firewall. The documented behaviour is specific to login. SSH, display managers, su-like tools, session managers and application-specific authentication can have their own rules or an already-authenticated session. Test the exact access path you mean to restrict, without closing your own administrative session.
5. Remove the system-wide gate
When maintenance is complete, remove the file from the same root-capable session:
sudo rm -- /etc/nologin
if test ! -e /etc/nologin; then
echo 'system login gate is removed'
fi
Removal is the recovery action. Do not leave an old notice in place: users keep getting refused by login until the file is gone. For a later maintenance window, write a fresh message after checking the date and expected end time.
Common traps
- Confusing the names. Setting an account's shell to
nologinaffects that account; creating/etc/nologinaffects unprivileged logins handled bylogin. - Testing the wrong thing. Running
nologinonly proves what the shell program itself does, not that SSH, a display manager or a directory service will select that shell. - Expecting a zero exit status. Refusal is reported by a non-zero exit; scripts should treat that as an intentional denial.
- Forgetting active sessions. Neither change logs out users already connected. Review sessions and processes separately before maintenance.
- Breaking automation. A service account can still be used by processes that do not start its login shell, but changing its shell can break tools that do. Verify the service after the change.
Done means
- Scope decided: one account or a temporary
logingate, explicitly. - Version checked: the installed
nologinpath and version. - Change verified: the account shell or
/etc/nologinconfirmed after the change. - Recovery path open: a root-capable session stays available.
- Sessions and services considered separately from the login-refusal change itself.