When a tool says “permission denied,” the blunt fix is to log in as root. The better fix is usually one elevated command — and a clear record of who ran it. That is what sudo is for. It lets a permitted user run a command as root (or another account) without sharing the root password, while the policy in /etc/sudoers decides who may do what.
You do not need every Defaults flag on day one. Start by listing your own privileges, run a single elevated command, then learn how to edit policy safely and delegate only the binaries a teammate actually needs. The sections below walk through the commands and sudoers patterns you will reach for most often — with enough context that each one feels intentional, not magical.
Warm-up: see what you can do
Your playground is the live policy on the machine in front of you. Before changing anything, ask sudo what it already allows.
Refresh the credential cache (you will be prompted for your password if the ticket has expired):
sudo -v
List the privileges granted to your account:
sudo -l
Typical shape (exact lines depend on your distro and group membership):
User alice may run the following commands on host:
(ALL : ALL) ALL
Confirm elevation works with a harmless identity check:
whoami
sudo whoami
alice
root
Note: If sudo -l fails with “is not in the sudoers file,” you are not in the policy yet — joining the sudo or wheel group (distro-dependent) or adding a drop-in rule is the fix, not guessing passwords. The troubleshooting section covers the usual messages.
Run a command with privilege
Most day-to-day use is a one-shot elevation: prefix the command you already know. Tools that read protected logs or descriptors — dmesg, journalctl, lsof — often need this when the first unprivileged pass looks empty.
Run a single command as root:
sudo dmesg -T | tail -n 5
Run as a different user with -u (useful for service accounts or “become deploy” workflows):
sudo -u www-data id
uid=33(www-data) gid=33(www-data) groups=33(www-data)
For a root login-style shell (loads root’s environment as if you logged in), use -i. For a shell that keeps more of your current environment, use -s:
sudo -i
# … work as root, then:
exit
sudo -s
Note: Prefer a one-shot sudo cmd over an interactive root shell when you only need one privileged action. A lingering root shell is easy to forget on a shared jump host. When you do need a shell, -i is usually clearer than -s because the environment matches “logged in as root.”
Environment and safety knobs
By default, modern sudo resets most of your environment (env_reset) and often forces a trusted secure_path so a malicious PATH cannot trick you into running a fake ls as root. That is a feature, not an inconvenience.
See whether your policy mentions those Defaults (output varies):
sudo -l | grep -E 'env_reset|secure_path|env_keep'
Preserve the calling environment only when you have a reason — for example, a build that needs HTTP_PROXY already set:
sudo -E env | grep -E '^(PATH|HOME|USER|HTTP_PROXY)='
Ask sudo to run a specific variable through without keeping everything:
sudo HTTP_PROXY="$HTTP_PROXY" apt-get update
Note: Blind sudo -E can leak secrets from your shell into a root process, or let a hostile LD_PRELOAD / PATH hitch a ride. Prefer passing named variables on the command line, or an explicit Defaults env_keep += "VAR" in sudoers for the few values you truly need.
Edit policy safely with visudo
The policy file is /etc/sudoers. A syntax error there can lock every admin out of elevation. Never open it with a plain editor on a live host — use visudo, which locks the file, opens your editor, and validates syntax before installing the change.
Edit the main policy (requires an existing sudo-capable account):
sudo visudo
Prefer a drop-in under /etc/sudoers.d/ so package upgrades do not fight your custom rules. Create or edit a named fragment with the same safety net:
sudo visudo -f /etc/sudoers.d/99-ops-deploy
Check a file without installing it (handy in config management dry-runs):
sudo visudo -cf /etc/sudoers.d/99-ops-deploy
/etc/sudoers.d/99-ops-deploy: parsed OK
List what is already included:
ls -l /etc/sudoers.d/
Note: sudo ignores drop-in names that contain . or end in ~. Stick to simple names like 99-ops-deploy. Keep a second root session open (or a break-glass console) while you test the first change so a bad rule does not strand you.
Read the sudoers language
A rule answers four questions: which user, on which host, as whom, may run what. The common short form looks like this:
alice ALL=(ALL:ALL) ALL
Broken down:
| Piece | Meaning |
|---|---|
alice | Who is allowed (user or %group) |
First ALL | On which hosts (often ALL for single-host boxes) |
(ALL:ALL) | Run as any user:any group |
Final ALL | Which commands |
Group membership is the usual way to grant “full admin” on desktop and server installs. Debian/Ubuntu traditionally use %sudo; many RHEL-family systems use %wheel:
%sudo ALL=(ALL:ALL) ALL
%wheel ALL=(ALL:ALL) ALL
Aliases keep long lists readable. Define once, reuse in rules:
User_Alias OPS = alice, bob, carol
Cmnd_Alias SERVICES = /bin/systemctl start nginx, /bin/systemctl stop nginx, /bin/systemctl reload nginx
OPS ALL=(root) SERVICES
Note: Always use absolute paths for allowed commands. Relative names are ambiguous and easy to game. When you allow a script, remember the user can often run anything that script invokes unless you constrain the script itself.
Fine-grained delegation
Least privilege means granting the binary and arguments someone needs — not a second root account. Put custom rules in a drop-in and keep ALL=(ALL) ALL for break-glass admins only.
Allow one user to restart a service without full root:
deploy ALL=(root) /bin/systemctl restart myapp, /bin/systemctl status myapp
Allow a group to run package tools only:
Cmnd_Alias PKG = /usr/bin/apt, /usr/bin/apt-get, /usr/bin/dpkg
%builders ALL=(root) PKG
NOPASSWD skips the password prompt for matching rules. Use it for automation accounts and tightly scoped commands — not for interactive humans with ALL:
monitoring ALL=(root) NOPASSWD: /usr/lib/nagios/plugins/check_disk
Tag a rule so it still requires a password for everything else, but not for the listed commands:
alice ALL=(ALL:ALL) ALL, NOPASSWD: /usr/bin/systemctl restart nginx
Note: NOPASSWD: ALL is almost never what you want on a laptop you leave unlocked. Prefer passwordless rules that name exact paths. If a command lets the user escape to a shell (vim, less, ftp, many interpreters), treating it as “safe sudo” is a false sense of security — they effectively have a root shell.
Common pitfalls and troubleshooting
When elevation fails, the message is usually more specific than “permission denied.” Read it, then check group membership and the effective policy.
Classic rejection when the account is missing from sudoers:
alice is not in the sudoers file. This incident will be reported.
Fix path (as an existing admin): add the user to sudo or wheel, or drop a rule in /etc/sudoers.d/. Confirm groups after a re-login:
groups
id
Host tags matter on multi-host sudoers shared via config management. A rule for web01 will not match on web02 if the hostname in the rule is literal. Prefer ALL on single-purpose hosts, or keep aliases in sync with inventory.
Include order and overrides: later matching rules can change behavior, and #includedir /etc/sudoers.d pulls fragments in lexical order. Name drop-ins with a numeric prefix (10-, 99-) so you know what wins.
Audit who used sudo recently via the journal or the auth log (path varies by distro):
sudo journalctl SYSLOG_IDENTIFIER=sudo -n 20 --no-pager
sudo grep sudo /var/log/auth.log | tail -n 20
# RHEL-family often uses:
# sudo grep sudo /var/log/secure | tail -n 20
For a wider ops loop after a failed privileged change, the Linux troubleshooting toolkit walks process, journal, and open-file checks in order.
Note: If you lock yourself out of sudo, recovery is a console or live session as root (or single-user mode), then visudo to repair the bad fragment. That is why drop-ins and a second open root session matter when you edit policy the first time.
Quick reference card
Keep this nearby until the habits become muscle memory:
| Goal | Command / snippet |
|---|---|
| Refresh ticket | sudo -v |
| List your privileges | sudo -l |
| Run as root | sudo cmd |
| Run as another user | sudo -u user cmd |
| Login-style root shell | sudo -i |
| Keep environment (sparingly) | sudo -E cmd |
| Edit main policy safely | sudo visudo |
| Edit a drop-in safely | sudo visudo -f /etc/sudoers.d/name |
| Syntax-check a file | sudo visudo -cf /path |
| Full admin via group | %sudo ALL=(ALL:ALL) ALL |
| One command for one user | user ALL=(root) /abs/path/to/bin |
| Passwordless scoped rule | user ALL=(root) NOPASSWD: /abs/path |
| Recent sudo audit | journalctl SYSLOG_IDENTIFIER=sudo / auth.log |
Practice drills
Use only a machine where you already have sudo (or a disposable VM). Do not paste experimental NOPASSWD: ALL rules onto a shared production host.
- Run
sudo -land identify whether your access comes from a user rule, a%grouprule, or both. - Elevate a read-only diagnostic (
sudo dmesg -T | tail -n 5orsudo journalctl -n 5) and confirmwhoamiis still your normal user afterward. - Run
idas another account that exists on the box withsudo -u … id(for examplenobodyor a service user). - Open a throwaway drop-in with
sudo visudo -f /etc/sudoers.d/99-practice-readonly, add a comment-only line (# practice), save, then confirmvisudo -cfreports parsed OK. Remove the file when finished if you do not want it kept. - Draft (do not apply on production unless you mean it) a least-privilege rule that lets user
deployrestart one systemd unit — absolutesystemctlpath, noALLcommands.
When you are ready to compare, here are solid answers — not the only ones, but clear and portable:
sudo -l
sudo dmesg -T | tail -n 5
whoami
sudo -u nobody id
sudo visudo -f /etc/sudoers.d/99-practice-readonly
sudo visudo -cf /etc/sudoers.d/99-practice-readonly
sudo rm -f /etc/sudoers.d/99-practice-readonly
# draft rule for drill 5 (absolute paths; adjust unit name):
# deploy ALL=(root) /bin/systemctl restart myapp, /bin/systemctl status myapp
If you can work through those five comfortably, you already cover most real sudo work: inspect what you are allowed to do, elevate one command at a time, edit policy only through visudo and drop-ins, and delegate named binaries instead of handing out full root. Start with sudo -l, prefer one-shot elevation over root shells, and treat NOPASSWD and ALL as deliberate choices — not defaults.