You wrote a script, it works fine when you run it in your terminal, then you move it to cron or a CI pipeline and it explodes with sudo: no tty present and no askpass program specified. Annoying, and the error message doesn't exactly explain itself.
The error means exactly what it says: sudo wants to prompt you for a password, but there's no terminal attached to prompt through, and no askpass helper available to pop up a GUI dialog. Sudo can't ask, so it gives up.
The real fix is to stop sudo from needing to ask. You do that by granting passwordless sudo for the specific commands your script runs — and nothing else.
Step 1: Open sudoers safely with visudo
Never edit /etc/sudoers directly with nano or vim. A syntax error locks you out of sudo entirely, and recovering from that is a bad afternoon. Use visudo, which validates the file before saving.
sudo visudo
You should see the sudoers file open in your default editor. On Ubuntu 22.04 that's usually nano. Scroll to the bottom — do not touch the top section.
Step 2: Add a NOPASSWD rule for the user and commands
Say your cron job runs as user deploy and needs to restart nginx and run a systemctl reload. Add a line like this at the very bottom:
deploy ALL=(ALL) NOPASSWD: /bin/systemctl restart nginx, /bin/systemctl reload nginx
Save and exit. In nano that's Ctrl+O, Enter, then Ctrl+X. You should land back at your shell prompt with no error output. If you see a parse error, visudo caught it and you can fix the line right there.
Now test from a non-interactive shell:
sudo -u deploy sudo -n systemctl reload nginx
The -n flag tells sudo "don't prompt, just fail if you'd need to." If that command runs clean, your cron job will too.
Step 3: If you need a blanket rule (and why you probably don't)
Some people slap this in and move on:
deploy ALL=(ALL) NOPASSWD: ALL
That works. It also means the deploy user can run literally anything as root with no password. If that user account gets compromised through a leaked SSH key or a bad container image, your box is gone. I've seen this exact shortcut turn a minor web app RCE into full root on a production database server. List the specific binaries you need, with full paths.
Why this works
Sudo has three ways to get a password: a TTY, an askpass program (GUI or SSH_ASKPASS), or nothing because it doesn't need one. Cron runs with no TTY at all — the process has no controlling terminal, so sudo's normal prompt fails. CI runners, systemd services, and Docker containers have the same problem.
NOPASSWD removes the password requirement for the listed commands, so sudo never tries to prompt. The command executes, the script continues, and the error is gone. It's not a hack around the error — it's sudo doing what it's designed to do for automation.
Less common variations
requiretty is set globally. Older RHEL and CentOS installs (RHEL 6, some 7 configs) ship with Defaults requiretty in sudoers. Even with NOPASSWD, sudo refuses to run without a TTY. Check with sudo grep -i requiretty /etc/sudoers. If it's there, either remove it or override it per-user:
Defaults:deploy !requiretty
You actually want an interactive prompt. If the script is running under SSH and you want a password prompt to reach the user, set up SSH_ASKPASS. Create a script that echoes the password (yes, this is insecure, use it only for controlled environments), then:
export SUDO_ASKPASS=/usr/local/bin/askpass.sh
sudo -A systemctl restart nginx
Passwordless via stdin. If you're stuck and can't edit sudoers, you can pipe the password in with -S:
echo "$SUDO_PASS" | sudo -S systemctl restart nginx
The password ends up in shell history, process lists, and logs. Only use this in throwaway containers where you don't care.
Wrong sudoers syntax causes a silent failure. Confirm your rule actually applies by running sudo -l -U deploy as root. You'll see a list of allowed commands. If your NOPASSWD rule isn't in that list, sudo never saw it — check for a typo, wrong user, or a rule above yours that matches first. Sudo applies the last matching rule, so ordering matters.
Prevention
- Use
sudo -nin your test scripts. It fails fast if a password would be needed, so you catch the problem on your laptop instead of at 3 AM when a cron job goes silent. - Create a dedicated service account for automation. Don't give cron root access via your personal user — if your account is revoked or its password rotates, you'll break jobs you forgot existed.
- Keep a copy of sudoers in version control. Not the live file, a template. When someone adds a NOPASSWD rule six months from now, you'll see it in the diff.
- Log sudo command usage. Add
Defaults logfile=/var/log/sudo.logto catch anything unexpected running as root. - If you manage more than a handful of servers, use Ansible, Chef, or whatever config tool your team has. Hand-editing sudoers across 40 boxes always drifts.
One last thing: after you add a NOPASSWD rule, run your cron job manually with env -i to strip the environment. Cron gets a bare environment and no TTY, so this reveals problems your interactive shell hides.