ZetCode

Linux sudo Command

last modified October 23, 2026

The sudo command runs a command as another user, by default as root. Instead of sharing the root password, administrators grant individual users the right to run all or selected commands with elevated privileges. Users authenticate with their own password, and every command is logged. The rules are kept in the /etc/sudoers file.

The syntax is sudo [options] command. The examples use sudo 1.9.13 on Debian 12. They were run in interactive terminal sessions by the user bob, a member of the sudo group, on a host called box; the users alice, carol, and deploy have various other rights. Passwords are typed but not echoed, so they do not appear in the transcripts.

Running a command as root

We put sudo in front of a command to run it with root privileges.

whoami
sudo whoami
sudo id

The first sudo asks for bob's own password, not the root password. The second one does not ask at all, because sudo remembers a successful authentication for 15 minutes. Some distributions, such as Fedora, also print a short lecture about responsible use the first time a user runs sudo; Debian and Ubuntu build sudo without it.

$ whoami
bob
$ sudo whoami
[sudo] password for bob:
root
$ sudo id
uid=0(root) gid=0(root) groups=0(root)

Who may use sudo

On Debian and Ubuntu, members of the sudo group may run any command as root; on Fedora, RHEL, and Arch Linux, the group is called wheel.

id
getent group sudo

The id command shows that bob belongs to the sudo group. To grant a user full administrator rights, we add them to the group with sudo usermod -aG sudo user; the change takes effect at the user's next login.

$ id
uid=1002(bob) gid=1003(bob) groups=1003(bob),27(sudo)
$ getent group sudo
sudo:x:27:bob

Credential caching

The cached authentication is valid for one terminal session. A few options manage it.

sudo -v
sudo -n true && echo "credentials cached"
sudo -k
sudo -n true
echo $?

The -v option (validate) authenticates and refreshes the cache without running a command. The -n option (non-interactive) never asks for a password; when one is needed, sudo fails instead, which is what scripts and cron jobs need. The -k option (kill) discards the cached credentials, so the next sudo asks again. A new terminal window has its own cache, so it asks for the password too.

$ sudo -v
[sudo] password for bob:
$ sudo -n true && echo "credentials cached"
credentials cached
$ sudo -k
$ sudo -n true
sudo: a password is required
$ echo $?
1

Wrong passwords

sudo allows three password attempts.

sudo ls /root

After each wrong password, sudo pauses briefly and asks again. After the third failure, it gives up, and the failed attempt is logged. Some distributions enable the insults option, which replaces the "Sorry, try again" message with a humorous remark.

$ sudo ls /root
[sudo] password for bob:
Sorry, try again.
[sudo] password for bob:
Sorry, try again.
[sudo] password for bob:
sudo: 3 incorrect password attempts

Users without sudo rights

Users who are not mentioned in the sudoers rules cannot use sudo. Here alice logs in with su and tries it.

su - alice
sudo whoami

sudo asks for the password first, so it cannot be used to probe the rights of an account without knowing its password. Older versions add "This incident will be reported." to the message; the attempt is logged either way.

$ su - alice
Password:
alice@box:~$ sudo whoami
[sudo] password for alice:
alice is not in the sudoers file.
alice@box:~$ exit
logout

Listing privileges

The -l option lists what the current user may run; -U lists the rights of another user, which requires root privileges.

sudo -l
sudo -l -U alice

The output first shows the Defaults settings that apply, and then the rules. (ALL : ALL) ALL means that bob may run any command as any user and any group. Checking sudo -l is the quickest way to find out why a command is refused.

$ sudo -l
[sudo] password for bob:
Matching Defaults entries for bob on box:
    env_reset, mail_badpass, secure_path=/usr/local/sbin\:/usr/local/bin\:/usr/sbin\:/usr/bin\:/sbin\:/bin, use_pty,
    logfile=/var/log/sudo.log

User bob may run the following commands on box:
    (ALL : ALL) ALL
$ sudo -l -U alice
User alice is not allowed to run sudo on box.

Running commands as another user

The -u option runs the command as a different user, and -g as a different group.

sudo -u alice whoami
sudo -u alice touch /home/alice/notes.txt
sudo ls -l /home/alice/notes.txt
sudo -g devs id -gn

Files created with sudo -u alice belong to alice, so this is the right way to work in another user's or a service account's directory, for example sudo -u postgres psql or sudo -u www-data php script.php. Creating the file as root would leave a root-owned file that alice could not modify.

$ sudo -u alice whoami
[sudo] password for bob:
alice
$ sudo -u alice touch /home/alice/notes.txt
$ sudo ls -l /home/alice/notes.txt
-rw-r--r-- 1 alice alice 0 Oct 23 19:28 /home/alice/notes.txt
$ sudo -g devs id -gn
devs

Root shells: -s and -i

For a longer administration session, sudo can start a root shell. The -s option starts a shell, and -i simulates a full login.

sudo -s
sudo -i
sudo -i pwd

With -s, the shell stays in the current directory, and the prompt ends with # to warn us that we are root. With -i, sudo runs the target user's login shell, which reads its profile files and changes to its home directory, just as if root had logged in. With a command, -i runs it in that login environment. Root shells should be left with exit as soon as possible; running individual commands with sudo leaves a better log.

$ sudo -s
[sudo] password for bob:
root@box:/home/bob/work# whoami; pwd; echo $HOME
root
/home/bob/work
/root
root@box:/home/bob/work# exit
exit
$ sudo -i
root@box:~# whoami; pwd; echo $HOME
root
/root
/root
root@box:~# exit
logout
$ sudo -i pwd
/root

Repeating a command with sudo

A common situation is a command that fails because it needs root privileges. In Bash, sudo !! repeats it with sudo.

cat /var/log/app/app.log
sudo !!

!! is Bash history expansion for the previous command. Bash prints the expanded command before running it. This works only in interactive shells, not in scripts.

$ cat /var/log/app/app.log
cat: /var/log/app/app.log: Permission denied
$ sudo !!
sudo cat /var/log/app/app.log
[sudo] password for bob:
2026-10-23 08:02:05 ERROR payment failed

sudo and redirection

A redirection is performed by our shell, which runs as the normal user, before sudo even starts.

sudo echo "level=info" > /etc/app.conf
echo "level=info" | sudo tee /etc/app.conf
echo "port=9090" | sudo tee -a /etc/app.conf > /dev/null
sudo sh -c 'echo "workers=4" >> /etc/app.conf'
cat /etc/app.conf

The first command fails without asking for a password: bob's shell cannot open /etc/app.conf for writing, so sudo never runs. The tee command, run with sudo, writes its input to the file; -a appends, and redirecting to /dev/null hides the copy that tee prints. Alternatively, sudo sh -c runs the whole command line, redirection included, in a root shell.

$ sudo echo "level=info" > /etc/app.conf
bash: /etc/app.conf: Permission denied
$ echo "level=info" | sudo tee /etc/app.conf
[sudo] password for bob:
level=info
$ echo "port=9090" | sudo tee -a /etc/app.conf > /dev/null
$ sudo sh -c 'echo "workers=4" >> /etc/app.conf'
$ cat /etc/app.conf
level=info
port=9090
workers=4

Environment and PATH

By default, sudo resets the environment (the env_reset option) and uses a fixed PATH (the secure_path option). This prevents a user from influencing a root command through variables.

export PATH=$HOME/bin:$PATH APP_MODE=test
hello
sudo hello
sudo printenv HOME USER SUDO_USER
sudo printenv APP_MODE; echo "exit: $?"
sudo --preserve-env=APP_MODE printenv APP_MODE
sudo APP_MODE=prod printenv APP_MODE
sudo "$(command -v hello)"

The hello script is in ~/bin, which is on bob's PATH but not on the secure path, so sudo cannot find it; giving the full path works. HOME and USER are set for root, and SUDO_USER records who ran sudo, which scripts can use. Our APP_MODE variable is removed. It can be kept with --preserve-env=APP_MODE (or all variables with -E), or set on the sudo command line; both are allowed only when the sudoers rules permit it, as they do for ALL.

$ export PATH=$HOME/bin:$PATH APP_MODE=test
$ hello
hello from bob
$ sudo hello
[sudo] password for bob:
sudo: hello: command not found
$ sudo printenv HOME USER SUDO_USER
/root
root
bob
$ sudo printenv APP_MODE; echo "exit: $?"
exit: 1
$ sudo --preserve-env=APP_MODE printenv APP_MODE
test
$ sudo APP_MODE=prod printenv APP_MODE
prod
$ sudo "$(command -v hello)"
hello from root

The sudoers file

The rules are stored in /etc/sudoers and in files in the /etc/sudoers.d directory.

sudo grep -Ev '^(#|$)' /etc/sudoers
sudo ls -l /etc/sudoers /etc/sudoers.d

A rule has the form who where=(as_whom) what. The line %sudo ALL=(ALL:ALL) ALL says that members of the sudo group (% marks a group), on all hosts, may run as any user and group any command. Defaults lines set options. The @includedir directive reads every file in /etc/sudoers.d whose name contains no dot and does not end with ~. The files are readable only by root. Our lab adds a logfile setting in 00-logfile.

The sudoers file must never be edited with an ordinary editor. A syntax error would break sudo for everyone, and on a system without a root password, nobody could fix it. The visudo command opens a copy in an editor and checks the syntax before installing it; sudo visudo -f /etc/sudoers.d/name edits a drop-in file.

$ sudo grep -Ev '^(#|$)' /etc/sudoers
[sudo] password for bob:
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
@includedir /etc/sudoers.d
$ sudo ls -l /etc/sudoers /etc/sudoers.d
-r--r----- 1 root root 1714 Apr 11  2026 /etc/sudoers

/etc/sudoers.d:
total 8
-r--r----- 1 root root   35 Oct 23 19:27 00-logfile
-r--r----- 1 root root 1096 Apr 11  2026 README

Granting specific commands

Rules can be limited to particular commands, given with full paths and optionally with arguments. Instead of editing interactively, we write a drop-in file, check it with visudo -c, and install it.

cat carol.sudoers
sudo visudo -cf carol.sudoers
sudo install -m 0440 carol.sudoers /etc/sudoers.d/carol
sudo -l -U carol

The -c option checks the syntax and -f names the file to check. The install command copies the file with the mode 0440 that sudo expects. The rule allows carol to view the application log with tail, either without options or with -n and a line count.

$ cat carol.sudoers
# carol may read the application log
carol ALL=(root) /usr/bin/tail /var/log/app/app.log, /usr/bin/tail -n * /var/log/app/app.log
$ sudo visudo -cf carol.sudoers
[sudo] password for bob:
carol.sudoers: parsed OK
$ sudo install -m 0440 carol.sudoers /etc/sudoers.d/carol
$ sudo -l -U carol
Matching Defaults entries for carol on box:
    env_reset, mail_badpass, secure_path=/usr/local/sbin\:/usr/local/bin\:/usr/sbin\:/usr/bin\:/sbin\:/bin, use_pty,
    logfile=/var/log/sudo.log

User carol may run the following commands on box:
    (root) /usr/bin/tail /var/log/app/app.log, /usr/bin/tail -n * /var/log/app/app.log

Testing the rule

Now carol logs in and tries the rule.

sudo tail /var/log/app/app.log
sudo cat /var/log/app/app.log
sudo tail -n 1 /etc/shadow /var/log/app/app.log

The permitted command works, and cat is refused. The third command shows a serious mistake in the rule: in sudoers, the * wildcard in arguments matches anything, including spaces and additional file names. carol could read /etc/shadow, which contains password hashes. Wildcards in command arguments should be avoided; list the exact allowed forms, such as /usr/bin/tail -n 50 /var/log/app/app.log, instead.

For the same reason, never allow editors, pagers, interpreters, or commands such as find or tar through sudo unless full root access is intended; most of them can start a shell or write arbitrary files.

$ su - carol
Password:
carol@box:~$ sudo tail /var/log/app/app.log
[sudo] password for carol:
2026-10-23 08:02:05 ERROR payment failed
carol@box:~$ sudo cat /var/log/app/app.log
Sorry, user carol is not allowed to execute '/usr/bin/cat /var/log/app/app.log' as root on box.
carol@box:~$ sudo tail -n 1 /etc/shadow /var/log/app/app.log
==> /etc/shadow <==
deploy:$y$j9T$FJY0fgPEMQQc1lnNn4TVJ0$wJzksVJUkGW561hv7IGkdyT2Tj3pUwgiLC3Qwi9eLC4:20749:0:99999:7:::

==> /var/log/app/app.log <==
2026-10-23 08:02:05 ERROR payment failed
carol@box:~$ exit
logout

Syntax errors

visudo -c catches errors before they take effect.

cat deploy.sudoers
sed 's/NOPASSWD:/NOPASSWD/' deploy.sudoers > bad.sudoers
sudo visudo -cf bad.sudoers
sudo visudo -cf deploy.sudoers && sudo install -m 0440 deploy.sudoers /etc/sudoers.d/deploy

We create a broken copy of the rule without the colon after NOPASSWD. visudo reports the line and column and exits with a non-zero status, so the && chain installs a file only after it has passed the check.

$ cat deploy.sudoers
# the deploy account runs the deployment script without a password
deploy ALL=(root) NOPASSWD: /usr/local/bin/deploy.sh
$ sed 's/NOPASSWD:/NOPASSWD/' deploy.sudoers > bad.sudoers
$ sudo visudo -cf bad.sudoers
[sudo] password for bob:
bad.sudoers:2:52: syntax error
deploy ALL=(root) NOPASSWD /usr/local/bin/deploy.sh
                                                   ^
$ sudo visudo -cf deploy.sudoers && sudo install -m 0440 deploy.sudoers /etc/sudoers.d/deploy
deploy.sudoers: parsed OK

Commands without a password

The NOPASSWD: tag allows commands to run without authentication. It is typical for service accounts and automation, which have no one to type a password.

sudo /usr/local/bin/deploy.sh
sudo -n whoami

The deploy user runs the deployment script without being asked for a password. The tag applies only to the listed command; for anything else, sudo wants a password, and with -n it fails immediately instead of asking, which is the right option in scripts.

$ su - deploy
Password:
deploy@box:~$ sudo /usr/local/bin/deploy.sh
deploying release as root
deploy@box:~$ sudo -n whoami
sudo: a password is required
deploy@box:~$ exit
logout

Aliases and groups

Larger configurations use aliases for lists of users and commands, and group rules. The team.sudoers file contains the following rules.

team.sudoers
Cmnd_Alias WEB = /usr/bin/systemctl restart nginx, /usr/bin/systemctl reload nginx
User_Alias WEBADMINS = alice, carol

WEBADMINS ALL=(root) WEB
%devs     ALL=(alice) NOPASSWD: ALL

Cmnd_Alias names a list of commands and User_Alias a list of users; alias names are written in capital letters. Members of the devs group may run any command as alice without a password.

sudo visudo -cf team.sudoers
sudo install -m 0440 team.sudoers /etc/sudoers.d/team
sudo -l -U carol | tail -n 4
sudo -l -U jano | tail -n 2

carol now has both her earlier rule and the web commands from the alias. jano, a member of devs, may act as alice. Files in /etc/sudoers.d are read in alphabetical order, and when several rules match, the last one wins.

$ sudo visudo -cf team.sudoers
[sudo] password for bob:
team.sudoers: parsed OK
$ sudo install -m 0440 team.sudoers /etc/sudoers.d/team
$ sudo -l -U carol | tail -n 4

User carol may run the following commands on box:
    (root) /usr/bin/tail /var/log/app/app.log, /usr/bin/tail -n * /var/log/app/app.log
    (root) /usr/bin/systemctl restart nginx, /usr/bin/systemctl reload nginx
$ sudo -l -U jano | tail -n 2
User jano may run the following commands on box:
    (alice) NOPASSWD: ALL

Editing files with sudoedit

sudoedit (or sudo -e) edits a protected file without running the editor as root.

ls -l /etc/app.conf
export SUDO_EDITOR='sed -i s/9090/8080/'
sudoedit /etc/app.conf
cat /etc/app.conf

sudoedit copies the file to a temporary file owned by the user, runs the editor as the user, and copies the result back as root. The editor is chosen by the SUDO_EDITOR, VISUAL, or EDITOR variable; here we use sed to make the change visible in a transcript, but normally it is vim or nano. Because the editor runs without privileges, its shell escapes and plugins cannot be abused, so allowing sudoedit /etc/app.conf in sudoers is safe, unlike allowing vim /etc/app.conf.

$ ls -l /etc/app.conf
-rw-r--r-- 1 root root 31 Oct 23 19:28 /etc/app.conf
$ export SUDO_EDITOR='sed -i s/9090/8080/'
$ sudoedit /etc/app.conf
[sudo] password for bob:
$ cat /etc/app.conf
level=info
port=8080
workers=4

Logging

sudo logs every command to the system log (on systemd systems, see journalctl _COMM=sudo). Our lab also writes to a file, set with Defaults logfile=/var/log/sudo.log.

sudo tail -n 6 /var/log/sudo.log

Each entry records the time, the user, the terminal, the working directory, the target user, and the full command. The sudoedit entry shows the editor command that was run.

$ sudo tail -n 6 /var/log/sudo.log
[sudo] password for bob:
Oct 23 19:29:41 : bob : TTY=pts/0 ; PWD=/home/bob/work ; USER=root ;
    COMMAND=/usr/bin/install -m 0440 team.sudoers /etc/sudoers.d/team
Oct 23 19:29:47 : bob : TTY=pts/0 ; PWD=/home/bob/work ; USER=root ;
    COMMAND=/usr/bin/sed -i s/9090/8080/ -- /etc/app.conf
Oct 23 19:29:51 : bob : TTY=pts/0 ; PWD=/home/bob/work ; USER=root ;
    COMMAND=/usr/bin/tail -n 6 /var/log/sudo.log

Best practices for sudo

Source

sudo manual, sudoers manual

In this article, we have used the sudo command to run commands as root and as other users, managed cached credentials, worked around the redirection pitfall, and written, checked, and tested sudoers rules. See also the passwd and file permissions articles.

Author

My name is Jan Bodnar, and I am a passionate programmer with extensive programming experience. I have been writing programming articles since 2007. To date, I have authored over 1,400 articles and 8 e-books. I possess more than ten years of experience in teaching programming.

List all Linux tutorials.