DevOps & Workflow17 min read

    How to add a user to sudoers in AlmaLinux or Rocky Linux

    By DanaServer Monitoring & Linux
    Share

    "How do I add a user to sudoers?" is one of the first questions asked on any new AlmaLinux or Rocky Linux box — usually right after you've created the non-root account you actually plan to log in as. The literal answer is a single command. The useful answer depends on what you actually want: full sudo with a password, full sudo without one, a narrow grant for a specific command, or a service-account grant that can only run one script. This guide walks through every common shape of that grant on RHEL-derived distros, the safe way to edit sudoers, how to verify the grant works, and how to avoid the classic mistakes that lock you out of the box.

    Both AlmaLinux and Rocky Linux are RHEL rebuilds, so everything below applies identically to both — and to RHEL, CentOS Stream, and Oracle Linux 8/9. The commands assume version 8 or 9; the only behavioral differences from older RHEL/CentOS 7 are noted inline.


    The wheel group: the canonical answer

    On AlmaLinux and Rocky Linux, the group that grants sudo is wheel. The default /etc/sudoers already contains the line that lets wheel members run anything with a password:

    %wheel  ALL=(ALL)       ALL
    

    So adding a user to sudoers is, in the common case, just adding them to wheel:

    sudo usermod -aG wheel alice
    

    That's it. After Alice logs out and back in (or starts a new shell), she can run sudo for any command and be prompted for her own password.

    Two details matter:

    • -a is not optional. usermod -G wheel alice (without -a) replaces Alice's group list with just wheel, dropping her from every other group she's in. Always use -aG.
    • The membership change requires a fresh login. Group membership is read at session start. id alice will show the new group immediately, but Alice's existing shell still has the old group set. New SSH sessions, new su - invocations, and new sudo calls pick up the change.

    To verify:

    id alice
    # uid=1001(alice) gid=1001(alice) groups=1001(alice),10(wheel)
    
    # Or just check group membership
    getent group wheel
    # wheel:x:10:alice
    

    Why "wheel"? It's a Unix tradition — the group named wheel (originally "big wheels", as in important people) has carried the "can become root" privilege on BSD and System-V-derived Unixes for decades. Debian/Ubuntu use sudo as the equivalent group name; RHEL/CentOS/Rocky/Alma use wheel. Functionally identical, just a different name.


    What "sudo" actually does, briefly

    A quick mental model that prevents most mistakes:

    • sudo <command> runs <command> as root by default (or as another user with -u).
    • Authorization is decided by /etc/sudoers (and any file dropped into /etc/sudoers.d/).
    • By default, sudo asks for the invoking user's password, not root's. (If your password isn't working, you may be thinking of su, which asks for root's.)
    • After a successful auth, sudo caches the credential for ~5 minutes (the timestamp_timeout default), so subsequent sudo calls in the same session don't re-prompt.
    • On AlmaLinux/Rocky 8/9, the default policy is targetpw=off and rootpw=off — your password authenticates you, not root's.

    The privilege itself is not granted by being in wheel directly — it's granted by the line in /etc/sudoers that says members of wheel can run things. Removing that line (or commenting it out) breaks wheel-based sudo even though the group still exists.


    Editing sudoers safely: always use visudo

    Never edit /etc/sudoers with a regular editor. Use visudo:

    sudo visudo
    

    visudo does three things vi /etc/sudoers does not:

    1. Locks the file so two admins can't simultaneously corrupt it.
    2. Validates the syntax on save — a typo that makes the file unparseable would otherwise lock everyone out of sudo, including root.
    3. Refuses to save invalid edits, prompting you to fix the error or abandon the change.

    If visudo finds an error, it shows:

    >>> /etc/sudoers: syntax error near line 99 <<<
    What now? [e]dit again, e[x]it without saving, exit and [Q]uit:
    

    Always pick e and fix it. Picking Q saves a broken sudoers file, which on a fresh machine where you haven't sudo'd yet may leave you with no escape.

    To use a specific editor (the default on EL is vi):

    sudo EDITOR=nano visudo
    

    To validate a sudoers file separately (e.g. one you're writing programmatically):

    sudo visudo -cf /etc/sudoers.d/alice
    

    visudo -c is the right thing to run in any deploy pipeline that writes sudoers files — fail the deploy on a non-zero exit rather than discovering it on the next reboot.


    Per-user grants without touching /etc/sudoers: /etc/sudoers.d/

    Editing /etc/sudoers directly works, but it's not the recommended path on modern RHEL-family distros. The base /etc/sudoers ends with:

    #includedir /etc/sudoers.d
    

    That tells sudo to read every file in /etc/sudoers.d/ as if it were appended to sudoers itself. The convention is one file per user or per role, named after the user/role:

    sudo visudo -f /etc/sudoers.d/alice
    

    Inside, write the grant directly:

    alice ALL=(ALL) ALL
    

    Save and exit. visudo -f validates the syntax of that specific file the same way visudo validates /etc/sudoers.

    This pattern has real advantages:

    • Easy to audit. ls /etc/sudoers.d/ is the list of everyone with custom sudo rights.
    • Easy to remove. rm /etc/sudoers.d/alice revokes the grant in one step. No risk of mangling the main file.
    • Configuration-management friendly. Ansible's template / copy modules, Puppet's file, and Terraform's local-exec all drop files cleanly into /etc/sudoers.d/. Editing /etc/sudoers from CM is fragile (idempotency is hard); dropping a managed file isn't.
    • No risk of corrupting /etc/sudoers. Even if the file in sudoers.d is malformed, sudo will skip it and log a warning rather than refuse to run entirely. (This is one of the reasons RHEL ships #includedir enabled by default.)

    A file in /etc/sudoers.d/ must not contain a . or ~ in the filename. The #includedir directive deliberately ignores files matching ~ (editor backups) and files containing . (e.g. .rpmsave, alice.bak). alice is fine; alice.conf is silently ignored. This trips people up regularly.

    Permissions matter too:

    sudo chmod 0440 /etc/sudoers.d/alice
    sudo chown root:root /etc/sudoers.d/alice
    

    visudo -f writes those permissions itself, but if you cp or mv a file into the directory, set them by hand.


    Common grant shapes

    Pick the narrowest one that solves your problem.

    Full sudo with a password (the standard admin grant)

    alice ALL=(ALL) ALL
    

    Equivalent to membership in wheel. Use this for human admins.

    Full sudo without a password

    alice ALL=(ALL) NOPASSWD: ALL
    

    NOPASSWD: removes the password prompt entirely for the matched commands. Convenient — and dangerous. A user with passwordless full sudo is effectively root with no extra friction; an attacker who lands a shell as that user is also effectively root with no extra friction.

    Reasonable uses: CI bastions, automation accounts, single-user homelab boxes. Unreasonable uses: production human admin accounts.

    If you must combine wheel-with-password and a NOPASSWD list for a specific user, the order in the file matters — later rules override earlier ones:

    %wheel  ALL=(ALL) ALL
    alice   ALL=(ALL) NOPASSWD: /usr/bin/systemctl restart nginx
    

    Now Alice is in wheel (so she gets full sudo with a password) plus can run systemctl restart nginx without a password.

    Narrow grant: just one or two commands

    This is usually what you actually want for service accounts:

    deploy ALL=(ALL) NOPASSWD: /usr/bin/systemctl restart myapp, /usr/bin/systemctl status myapp
    

    The deploy account can restart and check status of myapp without a password — and nothing else. If it tries sudo bash or sudo systemctl restart sshd, sudo refuses and logs the attempt.

    A few ergonomic notes:

    • Always use absolute paths. systemctl works in your shell because of $PATH; sudoers doesn't trust $PATH. Use /usr/bin/systemctl, not systemctl. (Use which systemctl to find it.)
    • Beware of arguments. NOPASSWD: /usr/bin/systemctl restart myapp permits exactly that command; sudo systemctl restart myapp.service (with the explicit suffix) is a different string and will be denied. To allow argument variations, drop the argument: NOPASSWD: /usr/bin/systemctl permits any systemctl invocation, which is much broader.
    • Wildcards are tempting and risky. NOPASSWD: /usr/bin/systemctl restart * looks tight but * matches spaces, so an attacker can pass restart x; rm -rf / and the rule still matches. Prefer enumerating exact commands or using Cmnd_Alias.

    For repeated lists, name them:

    Cmnd_Alias APP_OPS = /usr/bin/systemctl restart myapp, \
                         /usr/bin/systemctl reload myapp, \
                         /usr/bin/systemctl status myapp, \
                         /usr/bin/journalctl -u myapp
    
    deploy ALL=(ALL) NOPASSWD: APP_OPS
    

    Cleaner, easier to audit, easier to extend.

    Run as a different non-root user

    alice ALL=(postgres) /usr/bin/psql
    

    Now sudo -u postgres psql works, but sudo psql (which would default to root) does not. This is the right pattern for "let this admin run db commands as the db user" without giving them root.

    Run as another user, no password

    alice ALL=(www-data) NOPASSWD: ALL
    

    Alice can run anything as www-data without a password, but is not granted root.


    A complete worked example

    Create a new admin user from scratch on a fresh AlmaLinux or Rocky Linux box:

    # 1. Create the user (skip if it already exists)
    sudo useradd -m -s /bin/bash alice
    
    # 2. Set the password (alice will use this for sudo)
    sudo passwd alice
    
    # 3. Add to wheel — the canonical sudo group on EL
    sudo usermod -aG wheel alice
    
    # 4. (Optional) Drop in an SSH key so password isn't needed for login
    sudo mkdir -p /home/alice/.ssh
    sudo chmod 700 /home/alice/.ssh
    echo 'ssh-ed25519 AAAA... alice@workstation' | sudo tee /home/alice/.ssh/authorized_keys
    sudo chmod 600 /home/alice/.ssh/authorized_keys
    sudo chown -R alice:alice /home/alice/.ssh
    
    # 5. Verify
    id alice
    getent group wheel
    

    Now log in as alice from another terminal (don't close the existing root session yet — keep the safety net) and confirm:

    ssh alice@server
    sudo whoami
    # [sudo] password for alice: ********
    # root
    

    Once sudo whoami returns root, the grant works. Now you can disable root SSH login (PermitRootLogin no in /etc/ssh/sshd_config) and password authentication (PasswordAuthentication no) with confidence — alice has the keys to the kingdom and a key-based way in.

    The "keep the original session open" rule applies whenever you're changing auth: SSH config, sudoers, PAM. If the change locks you out, the open session is your way back in to fix it.


    Verifying a grant: what each user can actually do

    # What can the current user run with sudo?
    sudo -l
    
    # What can a specific user run? (run as root)
    sudo -l -U alice
    

    The output enumerates every matching rule, including which commands are NOPASSWD-eligible and which require a password. Run this any time you wonder whether a sudoers edit took effect — it's the authoritative answer, parsed exactly the way sudo itself parses the rules.

    Sample output for a wheel member:

    User alice may run the following commands on server:
        (ALL) ALL
    

    Sample output for a narrow grant:

    User deploy may run the following commands on server:
        (ALL) NOPASSWD: /usr/bin/systemctl restart myapp, /usr/bin/systemctl status myapp
    

    If a rule you wrote isn't showing up here, the file is most likely:

    • Misnamed (contains . or ~),
    • Has the wrong permissions (must be 0440, owner root:root),
    • Or has a syntax error and was rejected. sudo journalctl -t sudo and /var/log/secure show the parser warnings.

    Logging and auditing sudo use

    Every sudo call is logged. On AlmaLinux/Rocky:

    # Recent sudo activity
    sudo journalctl -t sudo --since "1 hour ago"
    
    # Or the classic auth log
    sudo tail -n 100 /var/log/secure | grep sudo
    

    Each entry includes the user, the working directory, the target user, and the command. For a more detailed audit trail (every command typed in a sudo session, with timestamps), enable I/O logging in sudoers:

    Defaults log_input,log_output
    Defaults iolog_dir="/var/log/sudo-io"
    

    Replay a session with sudoreplay. This is heavy and noisy — turn it on only for accounts that warrant it (shared admin accounts on regulated systems, post-incident forensics, etc.).

    For continuous monitoring of who's using sudo and when, ship /var/log/secure (or the journal) to a central log system and alert on patterns: a service account suddenly running sudo bash, multiple incorrect password entries in a row, or any sudo activity from a user who shouldn't have it. Xitoring's server monitoring collects log signals from EL hosts via Xitogent — pair it with systemd service health checks so a misconfigured sudoers file (one that breaks a service that runs sudo internally) is visible the moment it bites.


    Common mistakes that lock you out

    The classic ways to break sudo on a fresh AlmaLinux or Rocky box, and how to avoid them.

    Editing /etc/sudoers without visudo

    A typo in the main file makes sudo refuse to parse it at all. Every subsequent sudo call fails:

    >>> /etc/sudoers: syntax error near line 42 <<<
    sudo: parse error in /etc/sudoers near line 42
    sudo: no valid sudoers sources found, quitting
    

    Recovery: if you have an existing root shell open, visudo and fix it. If you don't, you need physical/console access — boot into single-user mode (rescue mode in EL parlance), edit /etc/sudoers, fix the syntax, reboot. This is the textbook reason to always use visudo.

    Removing yourself from wheel

    sudo gpasswd -d alice wheel    # removes alice from wheel
    

    If alice was the only sudo user and there's no root SSH login enabled, that's a lockout. Recovery: console boot to single-user mode, add yourself back. Or — better — keep one open root session whenever you're touching wheel membership.

    Files in /etc/sudoers.d/ that are silently ignored

    A file named alice.conf is skipped (the . rule). A file named alice~ is skipped (editor backup). A file with permissions 0644 instead of 0440 is rejected. The grant looks like it should work, cat-ing the file shows the right content, but sudo -l doesn't list it. Always:

    sudo ls -l /etc/sudoers.d/
    sudo visudo -c
    

    visudo -c cross-validates everything sudo would actually load.

    sudo works but sudo -i fails / interactive shell weirdness

    Some sudoers configurations restrict what you can do inside a sudo session. The requiretty default (off on modern EL but sometimes set in custom configs) blocks sudo from non-interactive sessions. If a CI job runs sudo systemctl restart myapp and it works locally but fails in CI with sudo: sorry, you must have a tty to run sudo, the fix is to remove Defaults requiretty from sudoers — or to allocate a TTY in the CI job (ssh -tt for SSH-driven runs).

    Sudo prompts for password when you expected NOPASSWD

    Two usual causes:

    • Rule order. Later rules override earlier ones. If %wheel ALL=(ALL) ALL comes after alice ALL=(ALL) NOPASSWD: /usr/bin/systemctl ..., alice gets prompted for a password because the wheel rule is the last one matching. Move the user-specific NOPASSWD line below the wheel line.
    • Caching. sudo caches credentials for 5 minutes. If you just ran sudo with a password, the next sudo won't prompt — that's not because of NOPASSWD, that's the cache. Run sudo -k to clear the cache and test fresh.

    Adding a user to sudo instead of wheel

    sudo usermod -aG sudo alice    # wrong on EL
    

    The sudo group exists on Debian/Ubuntu but not by default on RHEL/Rocky/Alma. Adding a user to a non-existent (or unprivileged) sudo group does nothing. The right group on EL is wheel. (usermod will silently create the sudo group entry on some configurations but won't grant any rights — there's no matching line in sudoers.)

    Using su and confusing it with sudo

    su switches user identity and asks for the target user's password (root's, by default). sudo runs a single command as another user and asks for your own password. They're two different tools. If "sudo isn't working" but you can su - into root, your sudo grant is wrong, not your password.


    Removing or revoking sudo

    To remove sudo from a user:

    # Remove from wheel (covers the wheel-based grant)
    sudo gpasswd -d alice wheel
    
    # And/or delete any per-user file in sudoers.d
    sudo rm /etc/sudoers.d/alice
    

    sudo -l -U alice after removal should show:

    User alice is not allowed to run sudo on server.
    

    For service accounts that are being decommissioned, the right cleanup is:

    sudo rm /etc/sudoers.d/<name>
    sudo userdel -r <name>          # deletes home directory too; check before running
    

    Operational tips

    • One file per user/role in /etc/sudoers.d/. Easier to audit, revoke, and put under configuration management. The main /etc/sudoers should ideally only contain defaults and the wheel rule.
    • Always run sudo visudo -c after any sudoers change made outside of visudo itself. Catches the silent-skip and parse-error cases before they hurt.
    • Keep one root session open whenever you're editing sudoers, PAM, or sshd_config. A second terminal is the cheapest insurance there is.
    • Prefer narrow grants over NOPASSWD: ALL. A service account that can only run a handful of commands is much less dangerous than one that can run anything as root without a password.
    • Never use shell metacharacters or wildcards in command paths. sudoers does not parse them the way a shell does, but it also doesn't escape them. Enumerate commands or use Cmnd_Alias.
    • Use sudo -l -U <user> to audit grants. It's the authoritative parsed view, not a cat of the file.
    • Log sudo use centrally. A sudo command from an account that shouldn't have it, or a flood of failed sudo attempts, is one of the cleanest pre-incident signals you'll get.

    Troubleshooting

    • alice is not in the sudoers file. This incident will be reported. — Alice is not in wheel and there's no per-user file. sudo usermod -aG wheel alice, then have alice log out and back in.
    • sudo: no tty present and no askpass program specified. — A NOPASSWD rule is missing for a non-interactive context, or requiretty is enforced. Either add NOPASSWD for the specific command, or allocate a TTY in your automation (ssh -t).
    • sudo: parse error in /etc/sudoers near line N. — The main sudoers file has a syntax error. Fix via visudo from an existing root shell, or boot to rescue mode if you're locked out.
    • A /etc/sudoers.d/<name> file exists but sudo -l -U <name> doesn't show it. — Check the filename for . or ~, check permissions are 0440 root:root, and run sudo visudo -c for parser warnings.
    • sudo works, but sudo -i or sudo bash doesn't. — A specific command grant doesn't include shell access. Either widen the grant (ALL instead of a specific command list) or accept that this account isn't supposed to have a root shell.
    • The user can sudo, but the password isn't accepted. — Sudo wants the user's password, not root's. If the user's password is genuinely correct and still rejected, check /var/log/secure for PAM errors and passwd -S <user> for account lock status.
    • sudo prints you must have a tty to run sudo from a script. — Add Defaults:<user> !requiretty (preferred) or run the script with a TTY (ssh -tt, script).
    • Group membership added but sudo still says "not in sudoers". — The user's session is using cached group info. id confirms group membership, but sudo reads it from the active session. Have them log out and back in (or run newgrp wheel for a temporary subshell with the new group).

    Summary

    To add a user to sudoers on AlmaLinux or Rocky Linux:

    1. The canonical answer: sudo usermod -aG wheel <user>. The default /etc/sudoers already grants wheel members full sudo with a password.
    2. For per-user grants, drop a file into /etc/sudoers.d/<user> with sudo visudo -f /etc/sudoers.d/<user>. Easier to audit, easier to revoke, configuration-management friendly.
    3. Never edit /etc/sudoers directly without visudo. A syntax error locks everyone out of sudo, including root.
    4. Pick the narrowest grant that works. NOPASSWD: ALL is convenient and rarely the right answer for human accounts.
    5. Use absolute paths in command grants and avoid wildcards.
    6. Verify with sudo -l -U <user> — the parsed, authoritative view of what a user can run.
    7. Log centrally and alert on anomalies. Misuse of sudo is one of the highest-signal events on a server.
    8. Keep an open root session whenever you edit sudoers, PAM, or sshd_config. It's the cheapest possible safety net.

    The mechanics are simple — a group, a file, a check. The discipline around them (visudo, narrow grants, central logging, an open safety session) is what separates a server you trust to run unattended from one that's one editor mistake away from a midnight rescue boot.