Running Jenkins Pipeline Steps as Root — sudoers & sudo -H Guide
Problem
Jenkins agents run as a non-privileged user (e.g., jenkins).
Scripts that require root access (certbot, SSH to remote hosts using /root/.ssh/) will fail with:
ERROR: This script must be run as root
or:
sudo: a terminal is required to read the password
sudo: a password is required
Solution Overview
Two things are needed:
sudo -H bashin the Jenkinsfile — to execute the script as root/etc/sudoers.d/jenkins-certbot— to allow passwordless sudo for those specific commands
Part 1 — sudo -H bash in Jenkinsfile
Why sudo bash and not just sudo ./script.sh?
When Jenkins runs sh '...', it launches a non-interactive shell without a TTY.
The cleanest way to run a script as root is:
sudo -H bash /path/to/script.sh [arguments]
What does -H do?
The -H flag tells sudo to set the HOME environment variable to the target user's home directory (root → /root).
Without -H:
HOME=/var/lib/jenkins # jenkins user's home
SSH looks for keys in: /var/lib/jenkins/.ssh/ ← does not exist
With -H:
HOME=/root # root's home
SSH looks for keys in: /root/.ssh/ ← correct location
This is critical when scripts use SSH/SCP to connect to remote hosts — they rely on /root/.ssh/config and /root/.ssh/id_* files.
Jenkinsfile example
steps {
sh '''
set -e
sudo -H bash /var/jenkins/_workdir/01-letsencrypt-renewal.sh --force
'''
}
Part 2 — sudoers Configuration
Why does sudo ask for a password?
By default, sudo requires a password for every user.
In a non-interactive Jenkins shell there is no TTY, so sudo cannot prompt — and fails.
How sudoers matching works
Sudoers matches the actual binary being executed, not the script path.
When Jenkins runs:
sudo -H bash /var/jenkins/_workdir/01-letsencrypt-renewal.sh --force
sudo sees:
command: /bin/bash
arguments: /var/jenkins/_workdir/01-letsencrypt-renewal.sh --force
This means the sudoers rule must reference /bin/bash, not the script itself.
Common mistake
# WRONG — sudo sees /bin/bash, not the script path
jenkins ALL=(root) NOPASSWD: /var/jenkins/_workdir/01-letsencrypt-renewal.sh
This rule will never match and sudo will still ask for a password.
Correct configuration
Create /etc/sudoers.d/jenkins-certbot:
# Allow jenkins to run certbot scripts as root via "sudo -H bash /path/to/script"
jenkins ALL=(root) NOPASSWD: /bin/bash /var/jenkins/_workdir/01-letsencrypt-renewal.sh *
jenkins ALL=(root) NOPASSWD: /bin/bash /var/jenkins/_workdir/02-copy-cert.sh
jenkins ALL=(root) NOPASSWD: /bin/bash /var/jenkins/_workdir/03-restart-haproxy.sh
Rule breakdown:
| Part | Meaning |
|---|---|
jenkins |
The user running the command (Jenkins agent user) |
ALL=(root) |
Can run as root on any host |
NOPASSWD: |
No password required |
/bin/bash |
The actual binary sudo checks |
/var/jenkins/_workdir/01-...sh * |
Specific script path; * allows any arguments (e.g., --force) |
/var/jenkins/_workdir/02-...sh |
No * — script takes no arguments, so rule is strict |
Installation
# Create the file
cat << 'EOF' > /etc/sudoers.d/jenkins-certbot
jenkins ALL=(root) NOPASSWD: /bin/bash /var/jenkins/_workdir/01-letsencrypt-renewal.sh *
jenkins ALL=(root) NOPASSWD: /bin/bash /var/jenkins/_workdir/02-copy-cert.sh
jenkins ALL=(root) NOPASSWD: /bin/bash /var/jenkins/_workdir/03-restart-haproxy.sh
EOF
# Set correct permissions (required by sudo)
chmod 440 /etc/sudoers.d/jenkins-certbot
# Validate syntax before applying
visudo -c -f /etc/sudoers.d/jenkins-certbot
Always validate with
visudo -cbefore saving.
A syntax error in sudoers can lock you out of sudo on the system.
Verification
After configuration, test from the Jenkins agent user:
# Switch to jenkins user
su - jenkins
# Test each command
sudo -H bash /var/jenkins/_workdir/01-letsencrypt-renewal.sh --force
sudo -H bash /var/jenkins/_workdir/02-copy-cert.sh
sudo -H bash /var/jenkins/_workdir/03-restart-haproxy.sh
None of these should prompt for a password.
Security Notes
| Practice | Why |
|---|---|
| Limit rules to specific script paths | Avoids granting unrestricted root bash access |
Use * only where arguments are needed |
Minimizes attack surface |
Set file permissions to 440 |
Required by sudo; world-readable sudoers files are rejected |
Validate with visudo -c |
Prevents locking yourself out |
Avoid ALL=(root) NOPASSWD: ALL |
Never grant unrestricted root sudo to service accounts |
Quick Reference
Problem: Jenkins can't run script as root
└─► Add: sudo -H bash /path/to/script.sh in Jenkinsfile sh block
Problem: sudo asks for a password in Jenkins
└─► Add sudoers rule for /bin/bash /path/to/script.sh (not the script directly)
Problem: SSH fails even after sudo (wrong keys / no config)
└─► Use sudo -H (sets HOME=/root so SSH finds /root/.ssh/)