Privilege Escalation with become
become is how Ansible runs a task as a different user — almost always root — after connecting over SSH as an unprivileged account. You log in as deploy, then become root through sudo for the tasks that need it, because SSHing directly as root is a security posture Ansible is built to avoid. The escalation is a separate, explicit step from the connection, and that separation is the whole idea worth understanding precisely.
Keeping the login and the privilege distinct is what lets a fleet run under least access without giving up the ability to install packages and write to /etc. This topic separates the connection user from the become user, walks the three knobs — become, become_user, become_method — covers per-play versus per-task escalation, the sudo-password flag, and why direct root SSH is the wrong default.
Connect as a User, Act as Root
Ansible's SSH connection and its privilege are two different things. ansible_user: deploy decides who logs in; become: yes decides who the task then runs as. You connect as a named, low-privilege account and escalate only for the work that needs root. This split is why you never put root in your SSH config — the login stays unprivileged and the escalation is an explicit, per-the-principle-of-least-access step.
Hold those two ideas apart and most of become follows. The connection answers "how do I get onto the box," the escalation answers "as whom do I run this task once I am there." They are configured separately, logged separately, and failing one is a different problem from failing the other.
deploybecome via sudodeploybecome, become_user, become_method
Three settings control escalation. become: yes turns it on. become_user: sets the target identity — root by default, but you set it to postgres to run a task as the database owner. become_method: picks the mechanism, with sudo the default and su and others available for hosts that require them. Together they answer on, as whom, and by what means.
- name: Configure the database tier hosts: db become: yes # escalate to root for most tasks become_method: sudo tasks: - name: Install PostgreSQL 16 ansible.builtin.apt: name: postgresql-16 state: present - name: Create the larkspur database become_user: postgres # this task runs as the DB owner, not root community.postgresql.postgresql_db: name: larkspur state: present
The play escalates to root for the package install, then one task overrides become_user to postgres for the database creation, which must run as the owner of the cluster. The connection user never changes — only who each task runs as once connected. That is the three knobs working together on one tier.
Per-Play vs Per-Task
Set become: yes on the play when most tasks need root, or on a single task when only it does. The common Larkspur pattern is exactly the one above: play-level root for installing packages and writing config, plus a per-task become_user: postgres for the database tasks that must run as the cluster owner. The play sets the default; the task sets the exception.
Choosing the level is about where the privilege belongs. If a play is mostly root work, declare it once at the play level and override the few tasks that differ. If only one task in an otherwise unprivileged play needs escalation, put become on that task alone. Both read clearly; scattering play-wide privilege across every task does not.
--ask-become-pass and the sudo Password
If the deploy account's sudo requires a password, the run needs --ask-become-pass — -K for short — to prompt for it once at the start. Without that flag on a password-required host, every escalated task fails at once, because sudo is waiting for a password that never comes. The alternative is sudoers config that grants the automation passwordless sudo for the specific commands it runs.
The failure mode is distinctive: not one task failing, but every escalated task failing together with a become-password error. When you see that pattern, the fix is almost always a missing -K or a sudoers entry that does not grant what the run needs. Knowing the shape of the failure turns a confusing wall of errors into a one-line fix.
Why You Don't SSH as Root
Direct root SSH is disabled on a well-run server — PermitRootLogin no — and for good reasons that become respects. Logging in as root removes the per-user audit trail, so you cannot tell who did what; it hands full control to anyone who obtains the key; and it skips the sudo log that records every escalation. Connecting as a named user and escalating keeps that log honest and the blast radius scoped to what sudo permits.
This is why become exists as a separate step rather than a convenience you can shortcut with ansible_user: root. The named-user-plus-sudo path is more secure, more auditable, and survives a server that hardens its SSH config. Reaching for root login to skip an escalation step trades all of that away for a few saved keystrokes.
- Setting
ansible_user: rootand SSHing in as root to skipbecome— it works until the server disables root login, removes the audit trail, and turns a stolen key into total control. - Running a play that needs root without
become: yesand getting "Permission denied" on the firstapttask, then reading it as a module bug rather than a missing escalation. - Forgetting
--ask-become-passon a host whose sudo needs a password, so every escalated task fails at once waiting for a become password that never arrives. - Leaving
become_userat the default root for a PostgreSQL task that must run as thepostgresuser, so the command runs as root and the database operation fails or leaves root-owned files. - Granting blanket passwordless sudo to
deployacross the fleet for convenience, widening what a compromised control node can do on every host.
- Connect as a named, unprivileged user like
deployandbecomeroot only for the tasks that need it, never SSHing directly as root. - Set
become: yesat the play level for tiers where most tasks need root, and use per-taskbecome_user:such aspostgresfor the exceptions. - Use
--ask-become-passwhen sudo requires a password, or scope passwordless sudo narrowly to the specific commands the automation needs rather than blanket NOPASSWD. - Keep
become_method: sudoas the default and change it only for hosts that genuinely requiresuor another method, so escalation behavior stays uniform across the fleet. - Read an all-tasks-fail-at-once escalation error as a sudo-password or sudoers problem, not a per-task bug, and fix it at the
-Kor sudoers level.
become formalizes
sudoers NOPASSWD the host-side config that grants passwordless escalation
Knowledge Check
What is the difference between the connection user and the become user?
ansible_userdecides who logs in over SSH;becomedecides who the task then runs as once connected — two separate steps- They are the same setting written under two different names, both naming the SSH login account, kept around only for backward compatibility with older playbooks
- The connection user is who the tasks run as, and the become user only opens the initial SSH session
- The become user logs in over SSH first, then drops down to the connection user to do the actual work
What do become_user and become_method control?
become_usersets the identity a task runs as (root by default, or e.g.postgres);become_methodsets the mechanism, defaulting tosudobecome_usersets the SSH login name used to open the connection andbecome_methodsets the TCP port the SSH transport connects on, so the pair fully configures how the control node reaches the host- Both keywords only control the verbosity of the escalation log, not which identity actually runs the task
become_userpicks whether to use sudo or su, andbecome_methodnames the target account to run as
A run against a host whose sudo needs a password fails with every escalated task erroring at once. What is the likely fix?
- Pass
--ask-become-pass(-K) so the run prompts for the sudo password, or grant scoped passwordless sudo on the host - Switch
ansible_userto root and connect directly as the superuser, droppingbecomefrom the play, so that the run never has to clear a sudo password prompt on any host - Add
--limitto retry one host at a time until sudo caches the password across the runs - Lower the verbosity level, since the errors are merely cosmetic and the tasks actually ran fine
Why is connecting as a named user and escalating with become the right default over SSHing directly as root?
- It preserves a per-user audit trail and the sudo log, scopes the blast radius, and survives a host that sets
PermitRootLogin no - Direct root SSH is measurably slower, because root login sessions are rate-limited at the kernel level
- A root SSH session cannot execute Ansible modules at all, because the module interpreter refuses to launch under uid 0, which makes escalating with
becomefrom a named user mandatory - A named-user login removes the need for any SSH key, since become handles all of the authentication
You got correct