sudo: no tty present and no askpass — 30-Second Diagnosis and Recovery (cron, CI, Ansible)
It's 3 a.m. Instead of a deploy notification, a job-failure alert goes off. You open the cron log and find a single line.
sudo: no tty present and no askpass program specifiedHere's the 30-second summary. In a non-interactive environment with no terminal (tty), sudo tried to prompt for a password, had nowhere to take input, and failed. On an SSH shell with a human attached, sudo can display a prompt and accept a password. cron, systemd, CI, and Ansible have no tty to receive that prompt. So sudo dies immediately with "I need to ask for a password, but there's no tty to ask on, and no askpass program was specified either."
Once you know the principle, recovery is one of three options: ① Don't ask for a password at all (NOPASSWD), ② Turn off the setting that requires a tty (requiretty), ③ Specify an askpass helper that supplies the password. Assuming you're on-call, we'll go in order: diagnosis table, copy-paste recovery, then how to avoid the security traps.
30-second diagnosis table: Where did it blow up?
Once you know which environment it ran in, the first-line fix is obvious.
| Execution environment | tty present? | Typical symptoms / log location | First-line fix |
|---|---|---|---|
| cron job | none | /var/mail/$USER or journalctl -u cron, grep CRON /var/log/syslog | sudoers NOPASSWD |
| systemd unit | none | journalctl -u myapp.service -e | NOPASSWD, or run the unit as root |
| GitHub Actions / GitLab CI | none | Failed step in the CI job log | Hosted runners are usually passwordless; self-hosted runners need NOPASSWD |
Ansible become: true | none by default | MODULE FAILURE / sudo: a password is required | become_password, or remove requiretty |
Wrapped in su -c "sudo ..." | none | Script stderr | Drop the wrap + NOPASSWD |
The key point is that every non-interactive environment has no tty. cron and systemd spawn child processes without a tty, and CI runners are headless. GitHub Actions' default runners already grant passwordless sudo to the runner user, so you rarely hit this error there—but on a self-hosted runner you have to configure it yourself.
Three copy-paste recovery options
(A) Add NOPASSWD to sudoers correctly — the safest, canonical fix
Why it works: If sudo never asks for a password, it has no reason to look for a tty.
Don't edit /etc/sudoers directly; always use visudo to create a drop-in file. A syntax error can lock sudo itself and cut off server access.
sudo visudo -f /etc/sudoers.d/deployKeep the file contents scoped to a specific user and command, as tightly as possible:
# /etc/sudoers.d/deploy
deploy ALL=(root) NOPASSWD: /usr/bin/systemctl restart myappPermission and syntax checks are mandatory:
sudo chmod 0440 /etc/sudoers.d/deploy
sudo visudo -c # syntax OK check
sudo -l -U deploy # deployIf the mode isn't 0440, sudo will ignore the file or emit a warning.
(B) Remove Defaults requiretty and how to verify
Why it works: requiretty is a hard rule: "sudo may only run from a tty." Non-interactive environments have no tty, so if this rule is on, you're blocked every time.
It's sometimes enabled by default on older CentOS/RHEL. Check and comment it out:
# /etc/sudoers (visudo )
-Defaults requiretty
+# Defaults requirettysudo grep -R requiretty /etc/sudoers /etc/sudoers.dNote that in recent sudo and OpenSSH, requiretty is off by default, so on a modern distro (A) alone usually solves it.
(C) SUDO_ASKPASS + sudo -A — supply the password via a helper
Why it works: Instead of a tty, you tell sudo about a helper program that prints the password to stdout.
cat > /usr/local/bin/askpass.sh <<'EOF'
#!/bin/sh
echo "$MY_SECRET"
EOF
chmod 700 /usr/local/bin/askpass.sh
export SUDO_ASKPASS=/usr/local/bin/askpass.sh
sudo -A systemctl restart myapp⚠️ Warning: This approach can leak the password into environment variables, the process list, and the script file. Avoid storing it in plaintext; inject it from Vault or a CI secret if you can. Prefer (A) NOPASSWD, and treat (C) as a last resort.
Handling this in Ansible
become: true internally calls sudo and hits the same error. Two tips:
# ansible.cfg
[ssh_connection]
pipelining = True # sudo requiretty
# requiretty after# playbook — NOPASSWD
- hosts: web
become: true
tasks:
- name: restart app
ansible.builtin.systemd:
name: myapp
state: restartedIf you really need a password, inject ansible_become_password safely via --ask-become-pass or Vault.
Security trap: Bind NOPASSWD to least privilege
If you panic and drop this in, the incident starts:
deploy ALL=(ALL) NOPASSWD:ALL # ❌If the deploy account is compromised, that's an instant full root takeover. Three rules to keep the principle of least privilege (PoLP):
- Whitelist commands with absolute paths: specify the full path like
/usr/bin/systemctl. Justsystemctlcan be bypassed via PATH manipulation. - Pin the arguments too: lock it down to the target service, e.g.
NOPASSWD: /usr/bin/systemctl restart myapp. - Split by service in
sudoers.d: don't dump everything in one file; split by role (deploy,backup) for management and audit.
Always verify the actual grants with sudo -l -U deploy.
One war story: Years ago on-call, I slapped in NOPASSWD:ALL thinking "we'll tighten it later." That temporary setting got flagged in a security audit six months later. Scoping the command from the start is actually the fastest path. These days the standard is to version-control sudoers with Ansible/Terraform as IaC, and turn on audit logging (/var/log/sudo.log) at the same time.
Conclusion: 5-line recovery checklist
- Confirm where it blew up using the diagnosis table (cron/systemd/CI/Ansible)
- Add command-scoped NOPASSWD with
sudo visudo -f /etc/sudoers.d/<role> - Verify permissions and syntax with
chmod 0440+visudo -c - Confirm actual grants with
sudo -l -U <user> grepfor leftover requiretty; comment it out if present
Prevent recurrence: Version-control sudoers as IaC, and add a visudo -c syntax-check gate to the CI pipeline. That stops a single bad sudoers line from locking the whole server.
FAQ
Q. Why doesn't this error show up on GitHub Actions?
A. GitHub-hosted runners already grant passwordless sudo to the runner user. On a self-hosted runner, you have to set NOPASSWD in /etc/sudoers.d/ yourself to avoid this error.
Q. Doesn't removing requiretty weaken security? A. requiretty only forces a tty; it does not strengthen authentication. Its main effect is blocking automation. Get your security from a command whitelist, least privilege, and audit logging—not from requiretty.
Q. Should I use NOPASSWD or SUDO_ASKPASS? A. Prefer command-scoped NOPASSWD. The password is never stored anywhere, so it's safer. SUDO_ASKPASS can leak the password into processes and environment variables; treat it as a last resort.
Sources and checks vary by article. Only articles with a recorded check date, scope and sources below were checked, and only within that scope. For other articles, verify against the current docs for your version before applying anything, and email us about errors. Editorial policy · email us
This article has no recorded human check (date and scope) yet.
Comments
Be the first to comment.