A deploy script or CI job needs to run one sudo command with no human to type a password, and NOPASSWD looks like the obvious fix. We cover how to scope it correctly through a dedicated file in /etc/sudoers.d instead of editing /etc/sudoers directly - syntax, pre-install validation, file permissions, and exactly where the line sits between reasonable automation and a self-inflicted security hole.
A deploy script dies on sudo systemctl reload nginx because sudo is waiting for a password and nothing in a CI job or a cron run can type one. The fastest search result is "make sudo passwordless" - and most of what comes up is exactly the wrong version of that fix: ALL=(ALL) NOPASSWD:ALL, copy-pasted onto a box that's also running a public-facing service. Here's how to scope NOPASSWD to one specific command through a drop-in file under /etc/sudoers.d/ instead of editing /etc/sudoers directly, and where the line actually sits between sane automation and a hole an admin dug for themselves.
Summary. NOPASSWD is a tag in a sudo rule that removes the password prompt for one user running one specific command. Configure it as a separate file under
/etc/sudoers.d/, ownedroot:rootat mode0440:username ALL=(root) NOPASSWD: /usr/bin/systemctl restart nginx. Validate the file withvisudo -cf path/to/filebefore it goes anywhere near the live config - it catches syntax errors that would otherwise sit there silently. Legitimate uses: deploy scripts, CI/CD runners, systemd units that need exactly one privileged command with no interactive input. The real risk:NOPASSWD:ALLon the account a network-facing service runs as - a compromise of that service is a compromise of root, with no second gate. Write commands with full paths and don't leave the argument list empty, or NOPASSWD ends up granting more than you intended.
What NOPASSWD actually does
By default, sudo asks for a password before running a privileged command - not root's password, the caller's own. That's fine when a human is sitting at the terminal. A script, a systemd unit, or a CI runner has no terminal to type into, so a bare sudo call inside one of those either hangs waiting for input that will never arrive, or fails outright.
NOPASSWD is a tag in a sudo rule that removes exactly that prompt, for one user-command pair. A rule like:
deploy ALL=(root) NOPASSWD: /usr/bin/systemctl restart nginx
lets the deploy user run precisely systemctl restart nginx as root, with zero password prompts. Every other command that user runs through sudo still asks for a password as usual, unless it's covered by its own rule carrying the same tag.
Why not just edit /etc/sudoers directly
Technically nothing stops you from appending the rule straight into /etc/sudoers. In practice nobody does, for reasons that aren't superstition. A dedicated file under /etc/sudoers.d/ stays out of the way of sudo package upgrades and anyone else's edits to the shared file; a named file like 90-deploy-nginx is self-documenting in an audit and disappears cleanly the moment access needs revoking. And the standard editing path, visudo, validates syntax before it lets you save and refuses to write a broken file - edit /etc/sudoers with nano or vim directly and that safety net is gone.
Worth sizing the actual risk honestly, though: on our test box, a deliberately broken line appended to /etc/sudoers itself, bypassing visudo entirely, did not lock sudo out. Every sudo call printed the exact syntax error with a line number, to both the log and the terminal, and kept working with whatever it could still parse. That's not a documented guarantee across sudo versions, and it's not a reason to skip validating syntax up front - just a reason not to panic if it happens.
Step 1. Write the rule in a scratch file, not in place
Draft the rule somewhere temporary first, so you can fix mistakes before they touch the live config:
sudo nano /tmp/deploy-nginx
Content is one line, full sudo rule syntax:
deploy ALL=(root) NOPASSWD: /usr/bin/systemctl restart nginx, /usr/bin/systemctl reload nginx, /usr/bin/systemctl status nginx
deploy- the user the rule applies to (a group needs a leading%);- the first
ALL- which hosts the rule applies to; leaving it asALLis fine unless you share one sudoers config across machines with different privilege sets; (root)- which account the command runs as; narrow it to a different account if root isn't actually needed;NOPASSWD:- the tag itself, applying to everything after it on the line until the next tag overrides it;- then a comma-separated command list, each entry with a full path:
/usr/bin/systemctl, not baresystemctl. This isn't a style preference - it's a syntax requirement. A bare command name is a parse error ("expected a fully-qualified path name") thatvisudo -cfcatches immediately, before the file is ever installed.
Arguments deserve a separate note. Leave them off entirely - just NOPASSWD: /usr/bin/systemctl - and the user can run that command with any arguments they choose, not the narrow set you had in mind. That's the gap between "can restart nginx" and "can run any systemctl subcommand against any unit on the box," and it's one of the most common NOPASSWD mistakes: one operation was the intent, the whole tool is what got granted.
Step 2. Validate the syntax before it touches the system
Before the file goes anywhere near /etc/sudoers.d/, check it with visudo's -c (check) and -f (file) flags:
sudo visudo -cf /tmp/deploy-nginx
Expected on success: /tmp/deploy-nginx: parsed OK. A typo - a stray comma, a missing colon after the tag, an unescaped special character in an argument - gets you a precise location instead: something like /tmp/deploy-nginx:1:9: syntax error, naming the line and column where the parser gave up. Don't move on until you see parsed OK.
Step 3. Install it with the right name, owner, and permissions
Once syntax is confirmed, move the file into place and set ownership and permissions immediately:
sudo cp /tmp/deploy-nginx /etc/sudoers.d/90-deploy-nginx
sudo chown root:root /etc/sudoers.d/90-deploy-nginx
sudo chmod 0440 /etc/sudoers.d/90-deploy-nginx
0440means "root and the root group can read, nobody can write without changing permissions first" - the confirmed default for every stock file under/etc/sudoers.d/on a clean Ubuntu install, and it's on you to set it for anything you drop in yourself;- looser permissions don't stop sudo from working, but it warns loudly on every call: in our test,
chmod 0777producedsudo: ...is world writable, and a non-root owner producedsudo: ...is owned by uid 1003, should be 0. Don't leave either in place - the whole point of the permission check is that nobody but root can quietly append themselves an extra rule; - the file name cannot contain a dot. This is documented behavior, not a style convention:
@includedirskips any name ending in~or containing a., specifically to avoid picking up editor backup and temp files. Verified live: a file named90-deploy-nginx.conf, correct rule, correct 0440 root:root ownership, installed with zero errors - and its rule never showed up insudo -l, no warning printed anywhere. It's one of the quietest ways to burn half an hour on "why isn't this working."
A numeric prefix like 90- isn't required, it's just how the ordering stays readable: files load in lexical order by name, and a consistent prefix tells you at a glance which rule wins if two ever overlap.
Step 4. Confirm the rule actually took effect
First check what sudo itself reports for that user:
sudo -l -U deploy
Expected: a line like (root) NOPASSWD: /usr/bin/systemctl restart nginx, ... in the list of permitted commands. If it's missing, the usual suspects are a dot in the file name, wrong permissions, or the file never actually landed in /etc/sudoers.d/.
The more honest test is running the command as that user with -n, which forces sudo to fail immediately instead of prompting interactively if a password turns out to be required:
sudo -u deploy sudo -n /usr/bin/systemctl status nginx
Expected: normal systemctl status output, no prompt. sudo: a password is required instead means either the rule didn't load, or the command you ran doesn't literally match what's in the rule.
That literal match is a recurring source of "NOPASSWD isn't working" reports where the configuration is actually fine. We verified this directly: a rule permitting /usr/bin/systemctl restart nginx still demanded a password for /usr/bin/systemctl restart nginx.service - same service, just with the explicit suffix - because sudo compares arguments character by character, not by meaning. If the calling script sometimes appends flags like --now or the full unit suffix, the rule needs to match the exact form the script actually invokes.
Where NOPASSWD is sensible automation, not a shortcut
NOPASSWD exists precisely for processes that have no way to type a password interactively and never will. A few cases where it's the right call:
- Deploy scripts. A CI/CD runner connects over SSH as a dedicated account and needs to restart exactly one service after a release ships - nothing else on the box.
- systemd timers and cron jobs. A backup script that needs to mount a partition or copy a system file on a schedule, unattended.
- Monitoring and health-check agents. Reading a service's status or a system log that requires root, read-only, nothing to change.
- Configuration orchestration (Ansible and similar). A control node runs a known, bounded set of administrative operations without a password prompt on every step of a playbook.
The common thread: the list of allowed commands is known in advance and doesn't shift at runtime. If you can enumerate exactly what the script needs and list it in the rule, that's the scenario NOPASSWD was built for.
Where NOPASSWD is a real risk, not a convenience
The danger isn't NOPASSWD itself, it's how wide the grant is. The line that shows up in half the tutorials online, and shouldn't be copied onto a production box:
deploy ALL=(ALL) NOPASSWD:ALL
This lets deploy run any command as any user, root included, with no password at all. Functionally that's equivalent to the account having no password gate for root-level actions - the only difference is sudo logs each command, which a direct root login wouldn't.
The scenario that matters most is when that same account is what a network-facing service - a web app, an API, a bot listening on a port - runs as. A remote-code-execution bug in that service, which web software has no shortage of, becomes a full server takeover through NOPASSWD:ALL in one step. Not "attacker gets deploy's privileges" - straight to root, one command, no second barrier. The gap between a working server and a fully compromised one is, literally, one line in sudoers.
The practical rule: never grant NOPASSWD:ALL to an account anything internet-facing runs as. For technical and deploy accounts, write out the exact command list they need - as shown in the steps above - instead of opening everything to save five minutes of configuration.
Rule | What it grants | Where it fits |
|---|---|---|
| exactly one command with exactly those arguments, as root | deploy script, CI/CD, systemd unit with a known, narrow job |
| any systemctl subcommand, any arguments | almost never on anything public-facing - too broad for most automation |
| literally any command as any user, no password | practically never on a server running a network service; at best an isolated CI runner already trusted with full access |
FAQ
How do I allow sudo without a password for one specific command?
Create a dedicated file under /etc/sudoers.d/ with a rule like username ALL=(root) NOPASSWD: /full/path/to/command. Validate it before install with sudo visudo -cf path/to/file, then set ownership to root:root and permissions to 0440. The file name must not contain a dot, or sudo silently ignores it.
Why not edit /etc/sudoers directly?
You can, but it drops the built-in protection: visudo validates syntax before saving and blocks a broken write, while a plain text editor saves whatever you type. Separate files under /etc/sudoers.d/ are also easier to version, roll back, and delete individually without touching the rest of the sudo config.
What permissions should a sudoers.d file have?
0440, owned by root:root: sudo chown root:root file && sudo chmod 0440 file. That's the shipped default for every file Ubuntu's packages drop there, and it's on you to set it for anything you add yourself. Overly permissive settings don't stop sudo from working, but it warns loudly on every invocation.
Is sudo NOPASSWD:ALL dangerous?
Yes, when it's granted to an account that a network-facing service runs as: a vulnerability in that service becomes a full root compromise with no second gate. For automation and technical accounts, enumerate the exact commands needed instead of using ALL.
How do I check sudoers syntax before saving?
sudo visudo -cf path/to/file for a single file, or sudo visudo -c to validate the entire active configuration, including everything under /etc/sudoers.d/. Success prints parsed OK; failure names the exact line and position where parsing stopped.
Key takeaways
- NOPASSWD is a tag on a sudo rule that removes the password prompt for one user and one command, not for sudo across the board.
- Configure it as a separate file under
/etc/sudoers.d/, never by editing/etc/sudoersdirectly - easier to roll back, easier to audit, doesn't collide with package updates. - Validate with
sudo visudo -cf pathbefore install, thenchown root:rootandchmod 0440after. - A dot anywhere in the file name under
/etc/sudoers.d/makes@includedirsilently skip it - no error, no warning. - Sudo matches commands and arguments literally:
systemctl restart nginxandsystemctl restart nginx.serviceare different rules to sudo, even though they're the same action to a human. - Legitimate territory: deploy scripts, CI/CD, systemd timers, and orchestration tools with a known, bounded command list.
- Real risk:
ALL=(ALL) NOPASSWD:ALLon the account a network-facing service runs as - one vulnerability in that service becomes root, with no barrier left in between.
What's next
If you're also sorting out the different ways to actually become root - sudo, sudo -i, sudo su -, and logging in as root directly - see root login on Ubuntu for how they differ. Need to change a password for a specific user or for root itself - changing passwords on Ubuntu covers all three cases. And the full list of common issues on a fresh Linux server lives in common Linux server problems.
