Before sudo became the default habit on most distributions, su was how everyone got root. I still use it constantly — mostly su - for a full login-shell switch, and su username when I need to genuinely become another account, not just run one privileged command. It’s a small tool, but the details around environment handling and PAM integration matter more than the short man page suggests. Here’s the complete rundown.
What su Does
su (substitute/switch user) changes the effective user ID and group ID of the current shell session to that of another account, prompting for that account’s password (unless you’re already root, in which case no password is required — root can su to anyone). Without an argument, it defaults to switching to root.
Syntax
Confirmed from su --help on util-linux 2.39.3:
Usage:
su [options] [-] [<user> [<argument>...]]
Change the effective user ID and group ID to that of <user>.
A mere - implies -l. If <user> is not given, root is assumed.
Full Option Reference
| Option | Long form | Description |
|---|---|---|
-, -l | --login | Start a full login shell — resets environment, changes to the target user’s home directory, sources their shell profile |
-c CMD | --command=CMD | Run a single command via the target user’s shell, then return |
--session-command=CMD | Like -c but without creating a new session | |
-m, -p | --preserve-environment | Keep the current environment variables instead of resetting them |
-w LIST | --whitelist-environment=LIST | Preserve only specific listed variables |
-g GROUP | --group=GROUP | Specify the primary group for the new shell |
-G GROUP | --supp-group=GROUP | Add a supplementary group |
-s SHELL | --shell=SHELL | Run a specific shell, if listed in /etc/shells |
-f | --fast | Pass -f to csh/tcsh shells (skip startup file processing) |
-P | --pty | Create a new pseudo-terminal for the session |
-h | --help | Show help |
-V | --version | Show version |
The Critical Difference: su vs su –
This is the single most important thing to understand about su, and it’s the source of more confusion than any other aspect of the command.
su username # switches UID/GID, but KEEPS your current shell environment
su - username # full login shell: new environment, new $HOME, runs target's .bash_profile
Without the dash, your $PATH, $HOME, and other environment variables often remain those of the original user, which can cause genuinely confusing bugs — running a command that resolves to the wrong binary because $PATH still points at your original account’s directories, or a script writing to the wrong $HOME. I default to su - essentially always, specifically to avoid this class of problem, and I’d recommend the same unless you have a specific reason to preserve environment.
Tested Reference Behavior
$ su --help
Usage:
su [options] [-] [<user> [<argument>...]]
Change the effective user ID and group ID to that of <user>.
A mere - implies -l. If <user> is not given, root is assumed.
Options:
-m, -p, --preserve-environment do not reset environment variables
-w, --whitelist-environment <list> don't reset specified variables
-g, --group <group> specify the primary group
-G, --supp-group <group> specify a supplemental group
-, -l, --login make the shell a login shell
-c, --command <command> pass a single command to the shell with -c
--session-command <command> pass a single command to the shell with -c
and do not create a new session
-f, --fast pass -f to the shell (for csh or tcsh)
-s, --shell <shell> run <shell> if /etc/shells allows it
-h, --help display this help
-V, --version display version
For more details see su(1).
Common Usage Patterns
Switch to root, full environment
su -
Switch to a specific user, full login environment
su - deploy
Run a single command as another user without an interactive shell
su -c "systemctl restart nginx" -
I use this pattern in scripts that need one privileged action embedded inside a larger workflow otherwise running as a normal user, without spawning a persistent shell.
Run a command as a specific non-root user (common in service scripts)
su -c "python manage.py migrate" deploy
Preserve your current environment while switching
su -m otheruser
Useful when you specifically need your own $PATH or environment variables (like a custom $http_proxy) to carry over into the new user’s shell — the opposite intent of su -.
How su Works Internally
su is a setuid-root binary — when you run it, it briefly runs with root privileges regardless of who invoked it, which is what lets it change the process’s UID/GID at all (an unprivileged process cannot normally change its own UID). This setuid mechanism is exactly why su‘s binary permissions and integrity matter so much from a security standpoint; it’s one of the small number of binaries on a typical system that’s a genuine privilege-escalation surface by design.
Authentication is handled through PAM (Pluggable Authentication Modules), configured in /etc/pam.d/su. This is why su‘s behavior can differ across distributions and hardened configurations — PAM modules control things like whether members of the wheel/sudo group can su to root without any restriction, whether failed attempts are logged or rate-limited (pam_tally2/pam_faillock), and whether su is restricted entirely to a specific group via pam_wheel.so.
A typical relevant line from /etc/pam.d/su:
auth required pam_wheel.so use_uid
Uncommenting this line restricts su (specifically to root) to members of the wheel group only — a common hardening step on security-conscious systems, and default behavior on many RHEL-family distributions.
Real-World Administration Examples
Restricting su to root to the wheel group (Debian/Ubuntu)
# Add user to the group allowed to su
usermod -aG sudo deploy # sudo group also commonly doubles as this gate on Debian-family
# Enable the wheel/sudo restriction in PAM
echo 'auth required pam_wheel.so use_uid' >> /etc/pam.d/su
Running a service as its dedicated service account
su -s /bin/bash -c "npm start" -l appuser
Explicitly specifying -s here matters if the target account’s default shell in /etc/passwd is /usr/sbin/nologin (common for service accounts, blocking direct interactive login) — -s overrides that for this one command.
Auditing recent su activity
grep " su " /var/log/auth.log | tail -20 # Debian/Ubuntu
journalctl _COMM=su --since "1 hour ago" # systemd-based systems
I check this regularly on shared systems as a basic accountability measure — knowing who switched to which account, and when.
Troubleshooting
“su: Authentication failure” — wrong password, or PAM configuration is blocking the account (e.g., pam_wheel.so restriction and the calling user isn’t in the required group).
“This account is currently not available” — the target user’s shell in /etc/passwd is set to /sbin/nologin or /bin/false; use -s /bin/bash to explicitly override for administrative access to a service account.
Environment looks wrong after switching (wrong PATH, wrong HOME) — you ran su user instead of su - user; always prefer the dash form unless you deliberately want to preserve environment with -m.
“su: must be run from a terminal” in certain restricted contexts — some PAM configurations or securetty restrictions block su to root from non-console terminals; check /etc/securetty if this applies to your setup (mostly relevant to direct root login restriction, less so to plain su).
su vs sudo vs Related Commands
| Command | Model |
|---|---|
su | Full session switch to another user’s shell/environment; requires that user’s password (or none, if you’re already root) |
sudo | Per-command privilege elevation; requires the invoking user’s own password (by default), governed by fine-grained rules in /etc/sudoers |
sudo -i | Functionally similar to su - but authenticated and audited through the sudo/PAM stack, with per-user logging |
machinectl shell / runuser | Alternative mechanisms for running a shell/command as another user, runuser specifically skips PAM’s su-specific authentication checks and is intended for use by already-privileged scripts, not interactive login |
login | Full authentication and session setup from a fresh terminal, the mechanism su - mimics internally for the login-shell parts |
I default to sudo for day-to-day single privileged commands specifically because it logs exactly what was run and by whom, without requiring the root password to be known or shared at all — a meaningfully better security posture on any multi-admin system. I reach for su - when I genuinely need an extended session as another account, not just one command.
Security Implications
su to root effectively requires either knowing the root password or already being root — which is precisely why many hardened distributions disable direct root login and password-based su to root entirely, funneling all privilege escalation through sudo with per-user auditing instead. If you administer a multi-user system, I’d strongly recommend:
- Locking the root account’s password (
passwd -l root) and relying exclusively onsudo - Restricting
suviapam_wheel.soeven if the root password remains usable, as defense in depth - Reviewing
/var/log/auth.logorjournalctlregularly for unexpectedsuattempts, successful or failed
Because su is setuid-root, any vulnerability in the binary itself is a serious local privilege-escalation risk — keep util-linux/shadow-utils (whichever package provides su on your distribution) patched promptly; this is core-system-package territory, not a low-priority update.
Distribution Compatibility
su is provided either by util-linux (Debian/Ubuntu) or shadow-utils (RHEL/Fedora), with essentially identical core behavior and flag sets across both. Default PAM restrictiveness differs meaningfully: RHEL-family distributions have historically shipped pam_wheel.so commented out by default (same as Debian), so out-of-the-box behavior tends to be similar, but hardened baseline images (CIS-benchmarked AMIs, for example) frequently enable the wheel restriction as part of their hardening profile. Always check /etc/pam.d/su on any system you’re newly administering rather than assuming defaults.
su in Scripts: Handling Arguments Correctly
A subtlety that catches people writing their first automation around su: everything after the target username is passed as arguments to the shell being invoked, not interpreted by su itself, which can produce confusing quoting issues if not handled carefully:
su -c "tar czf /backup/data.tar.gz /home/deploy/data" deploy
Here the entire quoted string is handed to deploy‘s shell as a single command via -c, exactly as if deploy had typed it interactively. Nesting quotes inside that string requires care — I generally prefer writing more complex operations into a standalone script file and invoking that via su -c /path/to/script.sh targetuser, rather than fighting escalating quote-escaping inside a one-liner, especially once variables need to be substituted from the calling context.
su Without a Password: The wheel Group Exception
One behavior worth calling out explicitly because it surprises people: if you’re already root, su - anyuser requires no password at all — root can become any account without authentication, by design, since root already has unrestricted access to the system regardless. This is different from a non-root user’s su attempt, which always requires the target account’s own password (subject to whatever PAM restrictions apply). This asymmetry is exactly why protecting root access itself — via SSH key-only login, disabling direct root SSH, and tightly controlling sudo/wheel membership — matters more than any individual su configuration detail; once an attacker has root, every other account on the system is trivially reachable through su alone.
Checking Who Last Used su
last | grep -i su
lastlog -u deploy
While su itself doesn’t create a traditional login record the way SSH sessions do, PAM’s session logging (through pam_unix or pam_systemd) typically records the switch in the system journal or /var/log/auth.log, and reviewing this periodically is a reasonable part of routine access auditing on any system where multiple administrators share elevated access.
Summary
su is a straightforward command with a subtle but critical detail — the dash — that determines whether you get a clean, correctly-scoped environment for the target user or a confusing hybrid of two accounts’ settings. Understanding that it’s PAM-driven and setuid-root explains both its flexibility (per-distribution authentication policy) and why it deserves the same security scrutiny you’d give any other privilege boundary on the system.
References
- Linux man-pages —
su(1): https://man7.org/linux/man-pages/man1/su.1.html - Linux-PAM System Administrators’ Guide: https://www.man7.org/linux/man-pages/man8/PAM.8.html
- Red Hat Documentation — Configuring
suaccess: https://access.redhat.com/documentation/ - Debian Wiki — sudo vs su: https://wiki.debian.org/sudo
