Automated jobs cannot type a password. A deployment that restarts a service, a backup script or a CI pipeline over SSH often needs one or two root commands with no prompt. The NOPASSWD tag in sudoers allows that. This guide shows how to set it up for a single user, how to limit it to specific commands, and what to watch out for.
Before you start
- A Linux VPS with Ubuntu 22.04 or 24.04, Debian 12, or AlmaLinux or Rocky Linux 8 or 9.
- A non-root user for the task, for example
deploy. See Add a new user on Linux. - That user logs in with an SSH key, not a password.
- A root shell open in a second window while you edit sudo rules.
Warning: Passwordless sudo for all commands means anyone who gets into that account is root instantly. Use it only for accounts protected by SSH keys, and prefer the limited version shown below.
Understand the risk
The sudo password is a second barrier. If an attacker steals an SSH key or finds a hole in a web app running as that user, the password still stops them from becoming root. NOPASSWD removes that barrier for the commands you list.
That leads to two simple rules:
- Grant
NOPASSWDonly for the exact commands the automation needs. - Never grant it to a user that runs a public-facing app, such as
www-data.
Create a drop-in file
Keep the rule in its own file under /etc/sudoers.d/. Always edit with visudo, which checks the syntax before saving:
sudo visudo -f /etc/sudoers.d/deployThe name must not contain a dot, so use deploy, not deploy.conf. The background is in Edit the sudoers file safely with visudo.
Option 1: limited NOPASSWD (recommended)
First find the full path of each command:
command -v systemctl/usr/bin/systemctlThen add a rule naming each allowed command in full:
deploy ALL=(root) NOPASSWD: /usr/bin/systemctl restart myapp, /usr/bin/systemctl reload nginxdeploy can now run exactly these two commands as root without a password. Any other sudo command still asks for the password, and fails if the account has none.
For a longer list, an alias keeps it readable:
Cmnd_Alias DEPLOY_CMDS = /usr/bin/systemctl restart myapp, \
/usr/bin/systemctl reload nginx, \
/usr/bin/systemctl status myapp
deploy ALL=(root) NOPASSWD: DEPLOY_CMDSTip: Avoid allowing commands that can start a shell or edit any file, such as vim, less, find, tar, bash or a script the user can write to. Each of these gives full root access in disguise.
Option 2: NOPASSWD for everything
Only for a trusted personal admin account with key-only SSH, add:
deploy ALL=(ALL:ALL) NOPASSWD: ALLSome cloud images set this up for their default user. It is convenient, but it means the SSH key is the only thing protecting root.
Run a script as root safely
If your deployment needs many root steps, put them in one script and allow only that script:
sudo install -o root -g root -m 0755 deploy.sh /usr/local/sbin/deploy-myappThe script must be owned by root and not writable by deploy. Otherwise the user could change it and run anything. Then allow only that path:
deploy ALL=(root) NOPASSWD: /usr/local/sbin/deploy-myappCheck the permissions on the file and the folder:
ls -l /usr/local/sbin/deploy-myapp-rwxr-xr-x 1 root root 812 Nov 9 10:12 /usr/local/sbin/deploy-myappUse it from automation
Pass -n (non-interactive) so a missing rule fails at once instead of hanging on a password prompt:
ssh deploy@203.0.113.10 'sudo -n /usr/bin/systemctl restart myapp'The command must match the rule exactly, including the full path when you wrote it in the rule. If it does not match, sudo prints a password is required.
Check it works
Validate the files, then log in as the user in a fresh session:
sudo visudo -c
sudo -lsudo -l lists the rule, for example:
User deploy may run the following commands on web01:
(root) NOPASSWD: /usr/bin/systemctl restart myapp, /usr/bin/systemctl reload nginxThen run an allowed command with sudo -n and confirm it succeeds, and a different command such as sudo -n whoami and confirm it is refused.
Troubleshooting
- Still asked for a password: another rule later in the files overrides yours. The last match wins. Check
sudo -l, and give your file a name that sorts later, such as90-deploy. - "sudo: a password is required" from a script: the command in the script differs from the rule, for example different arguments. Make them identical.
- "sudo: a terminal is required": the command needs a password because no rule matches. Fix the rule; do not work around it with
-S. - Your file is ignored: its name contains a dot or ends in
~. Rename it inside/etc/sudoers.d/.
Next steps
- Build tighter rules with aliases and testing: Give a user sudo access to specific commands only.
- Deploy from CI over SSH: Deploy from GitHub Actions over SSH.
