sudo

Run a command as another user, usually root

Updated 2026-08-31

sudo runs one command as another user, root unless you say otherwise, and asks for your password. su switches to another account for as long as you keep the shell open, and asks for that account's password. That difference is why a machine with several administrators uses the first: nobody has to know the root password, and /var/log/auth.log records which person ran what.

A fresh Debian install may have neither

su comes from util-linux, which Debian marks required, so it is always there. sudo is a separate package marked optional. If you gave the installer a root password, it does not install sudo and does not put your account in the sudo group; if you left the root password empty, it does both. That single choice, made once and forgotten, is behind most of sudo: command not found.

Debian's shipped /etc/sudoers grants the privilege through group membership, with one line: %sudo ALL=(ALL:ALL) ALL. Adding somebody is usermod -aG sudo alice, run as root, and it takes effect at their next login rather than immediately.

The environment does not come with you

sudo clears the environment and rebuilds a small one. PATH is replaced by the secure_path setting in /etc/sudoers, so a program you can run cannot always be run under sudo, and sudo finds /usr/sbin binaries your own shell does not.

Redirection is not part of the command

sudo tee file and sudo sh -c '… > file' exist because of one trap. In sudo cmd > /etc/file the shell opens /etc/file before sudo has run, and the shell is still you, so the write is refused and sudo never starts. The error comes from the shell and names the file rather than sudo or the command.

Sample files used on this page

Every example below was run against these files. Recreate them to follow along.

the sudo policy these examples run under /etc/sudoers.d is read in lexical order and the last matching rule wins, so the second file below overrides the first for one command and leaves everything else alone. The examples run as user; deploy is a second account with no privileges, so -u and su have somewhere to go that is neither root nor the account already running the command.

==> /etc/sudoers.d/user <==
user ALL=(ALL) NOPASSWD:ALL

==> /etc/sudoers.d/zz-page <==
user ALL=(ALL) PASSWD: /usr/bin/passwd
31 outputs, collapsed by default

Running a command as someone else

sudo takes a command, not a shell. Everything after the program name is that program's arguments, and anything the shell would do with them happens before sudo is involved.

Run one command as root

whoami; sudo whoami

The first line is you, the second the identity the command ran with. Only that one command is privileged: the shell you typed it into is unchanged.

Show output
user
root

See the full identity a command runs with

sudo id

Not just the user id. The group list is replaced too, so a command run under sudo is in root's groups and not in yours.

Show output
uid=0(root) gid=0(root) groups=0(root)

Run as some other account

cd /srv/app; sudo -u deploy id -un; sudo -u deploy pwd

-u takes any user, and root is only the default. The working directory is left alone, which matters when the command you are running takes a relative path.

Show output
deploy
/srv/app

Watch a command fail without sudo

ls /root; echo "exit=$?"

Root's home directory is mode 700, so an ordinary account cannot list it. ls exits 2 rather than 1: it distinguishes a serious failure from merely finding nothing.

Show output
ls: cannot open directory '/root': Permission denied
exit=2

Run the same thing with it

sudo ls -A /root

-A includes the dotfiles and leaves out . and .., which on a home directory is most of what there is to see.

Show output
.bashrc
.profile
.ssh

See that the exit status is the command's own

sudo false; echo "exit=$?"

sudo returns what the command returned, so a script can test it as normal. sudo refusing is also exit 1, with a message on stderr, so a script that has to know why it stopped cannot tell the two apart on the status alone.

Show output
exit=1

Refuse to prompt at all

sudo -n whoami

-n fails rather than asking for a password. Use it anywhere a prompt would hang: a cron job, a script running under a service manager, anything with no terminal attached.

Show output
root

See what -n does when a password is needed

sudo -n passwd --status deploy; echo "exit=$?"

The policy on this machine wants a password for passwd and no other command, so -n refuses this one and allowed the last one. A policy is written per command, not per account.

Show output
sudo: a password is required
exit=1

What the environment loses

env_reset is on by default on Debian, so a command under sudo does not inherit your environment. It gets a small one built from the policy instead.

Compare the two PATHs

echo "$PATH"; sudo printenv PATH

The second is secure_path from /etc/sudoers, and nothing you set can change it. This is why sudo finds /usr/sbin programs your own shell does not, and why a command in ~/bin or /opt is suddenly not found.

Show output
/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/games
/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin

Watch a variable fail to arrive

FOO=bar sudo printenv FOO; echo "exit=$?"

Nothing is printed and printenv exits 1, because FOO was removed on the way through. A script that sets a variable and then calls sudo expecting the command to see it will get no error, just different behaviour.

Show output
exit=1

Pass the environment through anyway

FOO=bar sudo -E printenv FOO

-E keeps what you had, and the policy can forbid it. Use it when a build or a proxy setting has to survive, and not as a habit: the reset exists so that a variable you did not think about, such as LD_PRELOAD, cannot follow a command into root.

Show output
bar

See where HOME points

printenv HOME; sudo printenv HOME

Debian's sudo sets HOME to the target user's, so a command that writes a dotfile writes it into /root. -H asks for exactly this and therefore changes nothing here; it is worth knowing on a system whose policy has been changed.

Show output
/home/user
/root

Compare a login shell with a plain one

cd /srv/app; sudo pwd; sudo -i pwd

-i is the closest thing to logging in as root: it starts a login shell, so the working directory becomes root's home and root's shell startup files run. Without it you stay where you already were.

Show output
/srv/app
/root

Keep the directory but take the shell

cd /srv/app; sudo -s pwd; sudo -s printenv HOME

-s runs your shell as root without the login part, so the directory stays and HOME still moves. Between -i, -s and neither, the question is whose startup files you want to run, and the answer is usually neither.

Show output
/srv/app
/root

Combine -i with another account

cd /srv/app; sudo -u deploy -i pwd; sudo -u deploy pwd

The same distinction applied to a service account: with -i you land in its home and read its profile, without it you are only borrowing its identity.

Show output
/home/deploy
/srv/app

See what a login shell does to PATH

sudo -i printenv PATH

Still secure_path. A login shell would normally build PATH from /etc/login.defs and root's profile, and the sudoers setting wins over both.

Show output
/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin

The redirect that still runs as you

The most common way sudo appears not to work. The shell sets up redirection before it runs anything, and the shell is not privileged.

Watch a redirect fail

printf 'sudo echo written > /etc/tips.conf\n' > try.sh
bash try.sh
echo "exit=$?"

The error names the file, not echo and not sudo, because sudo never ran: the shell tried to create /etc/tips.conf as user and stopped there. Run from a file here so the message carries a filename, as it would in a script of your own.

Show output
try.sh: line 1: /etc/tips.conf: Permission denied
exit=1

Write the file with tee

echo written | sudo tee /etc/tips.conf

tee does the opening, and tee is the thing running under sudo. It also prints what it wrote, so add > /dev/null when you do not want that echoed back.

Show output
written

Append instead of replacing

echo appended | sudo tee -a /etc/tips.conf

-a is >> to tee's >. Getting this wrong on a config file replaces it, so prefer tee over the shell form even once you know the trap.

Show output
appended

Give the redirect its own shell

sudo sh -c 'echo written > /etc/tips.conf'; sudo cat /etc/tips.conf

The other way round: run a shell under sudo and let that shell do the redirecting. Quote it carefully, because everything inside is expanded by the privileged shell rather than yours.

Show output
written

Finding out what you may do

List your own privileges

sudo -l

The defaults in force and every rule that matches you. Worth running on an unfamiliar machine before assuming either that you can do something or that you cannot.

Show output
Matching Defaults entries for user on deb1:
    env_reset, mail_badpass, secure_path=/usr/local/sbin\:/usr/local/bin\:/usr/sbin\:/usr/bin\:/sbin\:/bin, use_pty

User user may run the following commands on deb1:
    (ALL) NOPASSWD: ALL
    (ALL) PASSWD: /usr/bin/passwd

Try to read the policy directly

grep -c . /etc/sudoers; echo "exit=$?"

Mode 440 and owned by root, so you cannot read the file that decides what you may do. sudo -l exists because of this: it answers the question without handing over the policy.

Show output
grep: /etc/sudoers: Permission denied
exit=2

Read it with the privilege it describes

sudo grep -E "^(Defaults|root|%sudo)" /etc/sudoers

Debian's shipped policy, with the comments stripped out. Four defaults and two grants, and the last line is the one that matters: membership of the sudo group carries the privilege.

Show output
Defaults	env_reset
Defaults	mail_badpass
Defaults	secure_path="/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin"
Defaults	use_pty
root	ALL=(ALL:ALL) ALL
%sudo	ALL=(ALL:ALL) ALL

See who is in the sudo group

getent group sudo

Empty here, and user can still run sudo, because this machine grants it through a file in /etc/sudoers.d instead. Group membership is the usual route on Debian and not the only one, so ask sudo -l rather than inferring an answer from this.

Show output
sudo:x:27:

Check a sudoers file before it takes effect

sudo visudo -c -f /etc/sudoers.d/zz-page

visudo normally opens an editor and refuses to save a file that does not parse. -c -f runs only the check, for a file you wrote with something else or that a configuration management tool dropped in.

Show output
/etc/sudoers.d/zz-page: parsed OK

See what a typo looks like

printf 'user ALL=(ALL) NOPASWD:ALL\n' > bad.conf
sudo visudo -c -f bad.conf
echo "exit=$?"

NOPASWD is missing its S. A file like this dropped straight into /etc/sudoers.d can lock every account out of sudo at once. That is why the editing tool is the one that checks, and why editing over ssh with no second session open is a bad afternoon waiting to happen.

Show output
bad.conf:1:27: syntax error
user ALL=(ALL) NOPASWD:ALL
                          ^
exit=1

su, and when it is the one you want

su switches account rather than running one command, and it wants the target account's password. The examples below reach it through sudo, because a password prompt is the one thing a page of captured output cannot show you.

Switch to another account with a login shell

sudo su - deploy -c 'pwd; echo "$PATH"'

The - is the entire difference. It makes this a login shell, so the working directory becomes deploy's home and PATH is built from /etc/login.defs and that account's profile: no sbin directories, because those are for administrators.

Show output
/home/deploy
/usr/local/bin:/usr/bin:/bin:/usr/local/games:/usr/games

Switch without the login part

cd /srv/app; sudo su deploy -c 'pwd; echo "$PATH"'

Same account, and everything else inherited from whoever ran it. The directory has not changed and PATH is still the privileged one, so a process running as deploy is searching root's directories. Leaving the - off is the single most common mistake with this command.

Show output
/srv/app
/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin

Confirm where a login shell puts you

sudo su - deploy -c 'whoami; printenv HOME'

The identity and the home directory both moved, and a service account usually needs both before it will run anything: a program that writes to ~ and finds root's home has been started the wrong way.

Show output
deploy
/home/deploy

Become root interactively

su -

What you type on a machine with no sudo, and it asks for the root password. There is no output block because the command opens a shell and waits, so nothing a batch run can capture would be what you see.

See which packages the two come from

dpkg -S $(command -v su) $(command -v sudo)

They come from different packages, and only one of the two is part of every Debian system.

Show output
util-linux: /usr/bin/su
sudo: /usr/bin/sudo

Check which one Debian always installs

dpkg-query -W -f='${Package} ${Priority}\n' util-linux sudo

required means the system will not work without it. optional means a standard install has it and a minimal one may not, so a script that has to run on any Debian machine should stick to su, or to neither.

Show output
sudo optional
util-linux required