sudo needs a password but has no terminal to ask on — cron, CI, and scripts. The right fix is scoped NOPASSWD rules, never passwordless sudo everywhere.
cron/CI/docker exec have no TTY, so sudo can't prompt. If the user has no NOPASSWD rule, it aborts. Who it's running as determines the fix.
If you genuinely need sudo in a script, an askpass helper can supply the password — but NOPASSWD scoping is nearly always the better design.
echo '<user> ALL=(root) NOPASSWD: /usr/bin/systemctl restart myapp, /usr/bin/docker' | sudo EDITOR='tee' visudo -f /etc/sudoers.d/<user>-script
sudo crontab -e # runs AS root: no sudo needed inside the job
# e.g. gitlab-runner: one NOPASSWD line for the deploy script — audit what it can run
export SUDO_ASKPASS=/path/to/helper.sh ; sudo -A <cmd> # helper must echo the password — avoid on shared hosts
NOPASSWD: ALL is the mistake — scope it to exact binary paths so your deploy script can't become root's shell. systemd timers with User=root are a cleaner alternative to sudo-in-cron entirely.
That grants the runner (and anything that can write its scripts) unrestricted root. Scope NOPASSWD to the specific binaries the pipeline needs — a compromise turns into 'can restart one service' instead of 'owns the box'.
Install the job in root's own crontab (sudo crontab -e) and drop sudo entirely, or use a systemd timer with the right User=/Group=. Fewer privilege boundaries crossed at runtime.
Our most-documented failures, packaged as ready-to-ship starter kits: Docker, Kubernetes, and Terraform.
Browse the template store →One-time. Yours to modify. Instant download from the NinjaOps template store.