Introduction

The error "This command has to be run under the root user" is one of the most common Ansible errors for beginners. It occurs when a task requires root (superuser) privileges but the playbook is not configured to escalate privileges. This typically happens with package management, service control, user management, and system configuration tasks.

This article explains why this error occurs, all the ways to fix it, how Ansible privilege escalation works, and best practices for managing sudo access across your infrastructure.

Understanding the Error

When Ansible connects to a remote host, it runs tasks as the SSH user (often a regular non-root user). Many system operations require root privileges:

$ apt install nginx
E: Could not open lock file /var/lib/dpkg/lock-frontend - open (13: Permission denied)
E: Unable to acquire the dpkg frontend lock

The same thing happens in Ansible — the remote module runs as a non-root user and the underlying command fails:

TASK [rsync installed] **********************
fatal: [demo.example.com]: FAILED! => {
    "changed": false,
    "msg": "This command has to be run under the root user.",
    "results": []
}

Error Playbook

This playbook triggers the error because become: false prevents privilege escalation:

---
- name: Troubleshooting under the root user
  hosts: all
  become: false
  tasks:
    - name: rsync installed
      ansible.builtin.package:
        name: rsync
        state: present

Error Output

$ ansible-playbook -i inventory playbook.yml
PLAY [Troubleshooting under the root user] ********
TASK [Gathering Facts] *****************************
ok: [demo.example.com]
TASK [rsync installed] *****************************
fatal: [demo.example.com]: FAILED! => {
    "changed": false,
    "msg": "This command has to be run under the root user.",
    "results": []
}
PLAY RECAP *****************************************
demo.example.com : ok=1 changed=0 unreachable=0 failed=1

Solution 1: Enable become at Play Level

The most common fix — add become: true to the play:

---
- name: Install packages
  hosts: all
  become: true
  tasks:
    - name: rsync installed
      ansible.builtin.package:
        name: rsync
        state: present

Fixed Output

$ ansible-playbook -i inventory playbook.yml
PLAY [Install packages] ****************************
TASK [Gathering Facts] *****************************
ok: [demo.example.com]
TASK [rsync installed] *****************************
ok: [demo.example.com]
PLAY RECAP *****************************************
demo.example.com : ok=2 changed=0 unreachable=0 failed=0

Solution 2: Enable become at Task Level

If only specific tasks need root, apply become per task:

---
- name: Mixed privilege tasks
  hosts: all
  become: false
  tasks:
    - name: Check uptime (no root needed)
      ansible.builtin.command: uptime
      changed_when: false

    - name: Install rsync (root needed)
      ansible.builtin.package:
        name: rsync
        state: present
      become: true

    - name: Check disk usage (no root needed)
      ansible.builtin.command: df -h
      changed_when: false

Solution 3: Enable become at Role Level

---
- name: Deploy application
  hosts: webservers
  roles:
    - role: install_packages
      become: true
    - role: configure_app
      become: false

Solution 4: Set become in ansible.cfg

For environments where you always need privilege escalation:

# ansible.cfg
[privilege_escalation]
become = true
become_method = sudo
become_user = root
become_ask_pass = false

Solution 5: Command Line Flag

Override the playbook setting from the command line:

# Enable become
ansible-playbook -i inventory playbook.yml --become

# Specify become method
ansible-playbook -i inventory playbook.yml --become --become-method=sudo

# Specify become user
ansible-playbook -i inventory playbook.yml --become --become-user=root

# Prompt for sudo password
ansible-playbook -i inventory playbook.yml --become --ask-become-pass

How Ansible Privilege Escalation Works

When become: true is set, Ansible:

  1. Connects to the remote host as the SSH user
  2. Uploads the module to a temporary directory
  3. Executes the module via the become method (default: sudo)
  4. The module runs as the become user (default: root)
  5. Returns the result and cleans up

Become Directives

DirectiveDefaultDescription
becomefalseEnable privilege escalation
become_userrootUser to escalate to
become_methodsudoMethod: sudo, su, pbrun, pfexec, doas, dzdo, ksu, runas
become_flags(none)Additional flags for the become method

Precedence (highest to lowest)

  1. Task-level become
  2. Block-level become
  3. Role-level become
  4. Play-level become
  5. Command line --become
  6. ansible.cfg become

Tasks That Require Root

These operations almost always need become: true:

Task TypeModule Examples
Package managementapt, yum, dnf, package, pip (system)
Service managementservice, systemd
User/group managementuser, group
File ownership changesfile with owner/group
System configurationsysctl, hostname, timezone
Firewall rulesfirewalld, ufw, iptables
Mount operationsmount
Cron (for other users)cron with user
SELinuxselinux, seboolean

Tasks That Do NOT Require Root

Task TypeModule Examples
Reading filesstat, slurp, find (user-accessible paths)
Running commands as current usercommand, shell
Working with user's homecopy, template (to user's home)
Debuggingdebug, assert, fail
Setting factsset_fact, add_host
API callsuri, get_url (to user-writable paths)

Sudo Configuration on Target Hosts

For passwordless sudo (most common in automation):

# /etc/sudoers.d/ansible
ansible_user ALL=(ALL) NOPASSWD: ALL

For limited sudo (more secure):

# /etc/sudoers.d/ansible
ansible_user ALL=(ALL) NOPASSWD: /usr/bin/apt, /usr/bin/apt-get, /bin/systemctl, /usr/sbin/useradd, /usr/sbin/usermod

Testing Sudo Access

- name: Verify sudo access
  hosts: all
  become: true
  tasks:
    - name: Check effective user
      ansible.builtin.command: whoami
      register: whoami_result
      changed_when: false

    - name: Display effective user
      ansible.builtin.debug:
        msg: "Running as: {{ whoami_result.stdout }}"

Becoming a Non-Root User

You can escalate to any user, not just root:

- name: Run as application user
  ansible.builtin.command: /opt/myapp/bin/start
  become: true
  become_user: myapp

Alternative Become Methods

Using su Instead of sudo

- hosts: all
  become: true
  become_method: su
  become_user: root
  vars:
    ansible_become_password: "{{ vault_root_password }}"

Using doas (OpenBSD)

- hosts: openbsd_servers
  become: true
  become_method: doas

Common Mistakes

1. Missing become on Package Tasks

# ❌ Will fail
- ansible.builtin.apt:
    name: nginx
    state: present

# ✅ Add become
- ansible.builtin.apt:
    name: nginx
    state: present
  become: true

2. Sudo Password Required But Not Provided

fatal: [host]: FAILED! => {"msg": "Missing sudo password"}

Fix: Either configure passwordless sudo or use --ask-become-pass:

ansible-playbook playbook.yml --ask-become-pass

3. User Not in sudoers

fatal: [host]: FAILED! => {"msg": "deploy_user is not in the sudoers file."}

Fix: Add the user to sudoers on the target:

echo "deploy_user ALL=(ALL) NOPASSWD: ALL" > /etc/sudoers.d/deploy_user

4. become: true on Gathering Facts

If your SSH user can't read certain system info without sudo:

- hosts: all
  become: true       # Also affects fact gathering
  gather_facts: true

5. Mixing become Levels Unexpectedly

- hosts: all
  become: true   # All tasks run as root
  tasks:
    - name: Create file owned by deploy user
      ansible.builtin.file:
        path: /home/deploy/app.conf
        state: touch
        owner: deploy
      # Runs as root (from play-level become) - file is created as root then chowned

Security Best Practices

  1. Use become only where needed — don't set it at play level if only some tasks need it
  2. Limit sudo permissions — use /etc/sudoers.d/ to restrict which commands can be run
  3. Use Ansible Vault for sudo passwords if passwordless sudo is not an option
  4. Audit become usage — review playbooks for unnecessary privilege escalation
  5. Use become_user for application tasks — run app commands as the app user, not root
  6. Never SSH directly as root — use a regular user with sudo instead

Conclusion

The "This command has to be run under the root user" error means your task needs privilege escalation. The fix is adding become: true at the appropriate level — play, block, role, or task. For production environments, configure passwordless sudo for the Ansible user with minimal required permissions. Use task-level become instead of play-level when only specific tasks need root, and always follow the principle of least privilege.