How to Install Ansible on Ubuntu 26.04 and Write Your First Playbook

When you manage more than a handful of servers, running the same commands on each one over SSH stops scaling. Ansible solves that by describing the desired state of your infrastructure in YAML files, then applying that state across many hosts in parallel. There is no agent to install on the targets and no central server to maintain; Ansible only needs SSH access and Python on the managed hosts.
This guide explains how to install Ansible on Ubuntu 26.04, set up an inventory of remote hosts, run ad hoc commands, and write a small playbook that installs and starts Nginx on every host in a group.
Quick Reference
| Task | Command |
|---|---|
| Install Ansible (apt) | sudo apt install ansible |
| Show version | ansible --version |
| Ping all hosts | ansible all -m ping |
| Run an ad hoc command | ansible web -a "uptime" |
| Run a playbook | ansible-playbook site.yml |
| Check an established playbook | ansible-playbook --check --diff site.yml |
| Limit to one host | ansible-playbook site.yml --limit web1 |
| List inventory hosts | ansible-inventory --list |
| Edit a vault file | ansible-vault edit secrets.yml |
| Install a collection | ansible-galaxy collection install community.general |
Prerequisites
Before installing Ansible, make sure you have:
- A control node running Ubuntu 26.04 with a user with sudo privileges
. This is the machine where you run the
ansiblecommands. - One or more managed hosts you can reach over SSH from the control node. Ansible runs on the managed hosts using the Python interpreter that ships with Ubuntu, so no extra setup is required there.
- SSH key-based authentication from the control node to each managed host. Password authentication works but is slower and limits automation.
Step 1: Install Ansible
Ansible is available in the Ubuntu 26.04 repositories. The ansible package installs ansible-core plus a curated set of community collections:
sudo apt update
sudo apt install ansibleConfirm the install and the Python version Ansible is using:
ansible --versionThe output shows the Ansible version, the Python interpreter path, and the location of the default config file:
ansible [core 2.20.x]
config file = /etc/ansible/ansible.cfg
configured module search path = ['/home/user/.ansible/plugins/modules', '/usr/share/ansible/plugins/modules']
ansible python module location = /usr/lib/python3/dist-packages/ansible
python version = 3.14.xUbuntu 26.04 provides the Ansible 13 community package with ansible-core 2.20. The exact patch versions may be newer after regular package updates.
If you need a release that is newer than the Ubuntu package, install Ansible in an isolated environment with pipx. This avoids changing packages in the system Python environment:
sudo apt install pipx
pipx ensurepath
pipx install --include-deps ansibleStart a new shell after pipx ensurepath if the ansible command is not immediately available. The Ansible PPA does not currently publish an Ubuntu 26.04 package, so do not use it for this release.
Step 2: Create an Inventory File
The inventory is the list of hosts Ansible can talk to. Create a working directory for your project and a small inventory:
mkdir -p ~/ansible-demo
cd ~/ansible-demo
nano inventory.iniAdd the following content. Replace the IP addresses with your own:
[web]
web1 ansible_host=192.168.1.21
web2 ansible_host=192.168.1.22
[db]
db1 ansible_host=192.168.1.31
[all:vars]
ansible_user=ubuntu
ansible_python_interpreter=/usr/bin/python3The [web] and [db] sections define groups. Each entry is a host alias followed by connection variables. The [all:vars] block sets defaults for every host. Set ansible_user to the SSH user on the managed hosts (often ubuntu on cloud images or your own user on a private fleet).
Tell Ansible to use this inventory by default by creating a project-level config:
nano ansible.cfgAdd:
[defaults]
inventory = ./inventory.iniAnsible keeps SSH host-key checking enabled by default. Before the first Ansible run, connect to each server with SSH, verify its fingerprint, and accept the key:
ssh ubuntu@192.168.1.21Repeat this for each managed host. Verifying host keys protects the control node from connecting to an impersonated server.
Step 3: Test the Connection
Use the ping module to confirm Ansible can reach every host in the inventory. Despite the name, this module does not use ICMP; it logs in over SSH and runs a tiny Python module on the target:
ansible all -m pingA successful run prints a green pong for each host:
web1 | SUCCESS => {
"ansible_facts": {
"discovered_interpreter_python": "/usr/bin/python3"
},
"changed": false,
"ping": "pong"
}If you see UNREACHABLE, check the SSH key, the user name in ansible_user, and basic connectivity with ssh ubuntu@192.168.1.21.
You can scope commands to a group instead of all. For example, list disk usage on every web host:
ansible web -a "df -h /"The -a flag runs an ad hoc command using the default command module. Use -m shell instead when the command relies on pipes, redirection, or shell expansion.
Step 4: Write Your First Playbook
A playbook is a YAML file that describes the desired state of your hosts. Create one that installs Nginx and makes sure the service is running on every web host:
nano site.ymlAdd the following content:
- name: Configure web servers
hosts: web
become: true
tasks:
- name: Install Nginx
ansible.builtin.apt:
name: nginx
state: present
update_cache: true
- name: Ensure Nginx is running and enabled
ansible.builtin.service:
name: nginx
state: started
enabled: true
- name: Deploy a custom landing page
ansible.builtin.copy:
content: "<h1>Managed by Ansible</h1>\n"
dest: /var/www/html/index.html
owner: root
group: root
mode: "0644"The play targets the web group and uses become: true so each task runs as root through sudo. The three tasks install the nginx package, make sure the service is started and enabled at boot, and replace the default index page with a small marker.
Step 5: Run the Playbook
Check the YAML and playbook structure before changing the managed hosts:
ansible-playbook --syntax-check site.ymlWhen the syntax check passes, run the playbook:
ansible-playbook site.ymlThe output ends with a per-host summary that looks like this:
PLAY RECAP *********************************************************************
web1 : ok=4 changed=3 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
web2 : ok=4 changed=3 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0The changed=3 count means Ansible applied three of the four tasks (the gather facts task counts as ok only). Run the playbook in check mode after the initial configuration:
ansible-playbook --check --diff site.ymlThe check should report changed=0 because the hosts already match the playbook. Check mode is a simulation, and some modules do not fully support it, so review its output before relying on it for production changes.
Open http://<web_host_ip> in a browser and you should see the custom landing page.
Step 6: Use ansible-vault for Secrets
Most playbooks reference passwords or API tokens. ansible-vault encrypts variable files at rest so you can keep them in version control:
ansible-vault create secrets.ymlSet a strong password when prompted. Add encrypted variables to the file, for example:
db_password: "s3cret-value"--vault-password-file outside the repository.Reference the vault file from a play with vars_files and run the playbook with --ask-vault-pass or --vault-password-file:
- name: Configure web servers
hosts: web
become: true
vars_files:
- secrets.ymlRun the playbook and enter the vault password when prompted:
ansible-playbook site.yml --ask-vault-passAnsible Vault protects data at rest. Use no_log: true on tasks that may print decrypted secrets, and never commit a vault password file to version control.
Troubleshooting
UNREACHABLE errors on every host
Ansible cannot SSH into the targets. Run ssh ansible_user@host manually to confirm the key, the user, and the network path. Check that ansible_user in the inventory matches the user that owns the authorized SSH key.
sudo: a password is required
The remote user requires a password for sudo and you did not pass one. Run the play with --ask-become-pass or configure passwordless sudo for the deploy user on the managed hosts.
Tasks fail with Could not find python interpreter
The discovery process picked the wrong path. Set ansible_python_interpreter=/usr/bin/python3 in the inventory [all:vars] block to pin it to the Ubuntu default.
Host key verification failed
The server key is missing from ~/.ssh/known_hosts, or it changed since the previous connection. Connect with SSH and verify the fingerprint. If the server was rebuilt, remove only its stale entry with ssh-keygen -R 192.168.1.21, then reconnect and verify the new key.
Playbook reports changed on every run for the same task
A task is not idempotent. Re-check the module you used. For example, prefer ansible.builtin.copy or ansible.builtin.template over command: echo ... > file, because shell redirection always reports changed.
FAQ
What is the difference between Ansible and ansible-core?ansible-core is the engine and the small set of built-in modules. The ansible package adds a curated bundle of community collections on top of ansible-core. Most users install ansible and pull additional collections with ansible-galaxy as needed.
Do I need to install anything on the managed hosts?
Only Python and an SSH server, both of which ship by default on Ubuntu. See How to Install Python on Ubuntu 26.04
if you need to confirm the interpreter is present.
Can I run Ansible against the local machine?
Yes. Add a localhost ansible_connection=local entry to the inventory, or run a playbook with ansible-playbook -i localhost, -c local site.yml. The trailing comma after localhost tells Ansible to treat the value as an inline inventory rather than a file path.
How do I structure a real project?
Split logic into roles under a roles/ directory, group host variables under group_vars/, and host-specific values under host_vars/. Keep site.yml short and import roles per group. The Ansible documentation calls this layout a “best-practices project”.
Next Steps
You now have a working Ansible setup on Ubuntu 26.04 and a first playbook that configures a group of web servers. From here you can break tasks into roles, parameterize plays with variables and templates, and add CI to run ansible-playbook --check on every push. You can also use the same inventory and roles to prepare hosts before you install Docker on Ubuntu 26.04
.
Linuxize Weekly Newsletter
A quick weekly roundup of new tutorials, news, and tips.
About the authors

Dejan Panovski
Dejan Panovski is the founder of Linuxize, an RHCSA-certified Linux system administrator and DevOps engineer based in Skopje, Macedonia. Author of 800+ Linux tutorials with 20+ years of experience turning complex Linux tasks into clear, reliable guides.
View author page