If you manage more than a handful of Linux servers, you have probably hit the wall where SSH and bash scripts stop scaling. You patch one server, forget another. A config change goes on three nodes but misses a fourth. Ansible automation on Linux solves this by turning infrastructure tasks into repeatable YAML files that run across every server at once. No agents to install on managed nodes. No master server to maintain. Just SSH and a playbook. This guide takes you from a fresh install to your first real automation task.

How Ansible Works
Ansible runs from a control node, any Linux machine with Python and SSH access to your servers. You write tasks in YAML files called playbooks. When you run a playbook, Ansible connects to each target over SSH, runs a small Python script, then removes it. Nothing stays on the managed node after the run finishes.
This agentless design is one of Ansible’s biggest practical advantages. You do not need to pre-install anything on new servers before managing them. As long as SSH works and Python 3 is available, which it is on every modern Linux distribution, Ansible can manage it.
Four concepts cover most of what you need to start: inventory (the list of servers), playbooks (YAML files that define what to do), tasks (individual steps inside a playbook), and modules (built-in functions like apt, service, and copy). The full module list is on the Ansible module index.
Install Ansible on Your Control Node
Install ansible-core using pip for the most current version:
# Ubuntu / Debian
apt install python3-pip -y
pip3 install ansible-core
# RHEL / AlmaLinux
dnf install python3-pip -y
pip3 install ansible-core
# Verify
ansible --version
You should see ansible [core 2.20.x] in the output. The pip version stays more current than the package manager version, so pip is the better choice for most setups. If you prefer package-managed installs:
apt install ansible -y # Ubuntu/Debian
dnf install ansible-core -y # RHEL/AlmaLinux
Set Up SSH Key Authentication

Ansible connects over SSH. Before running any playbook, you need passwordless SSH access from your control node to every managed host. Generate a key if you do not have one:
ssh-keygen -t ed25519 -C "ansible-control"
Copy your public key to each managed server:
ssh-copy-id user@server1
ssh-copy-id user@server2
Test it works without a password prompt:
ssh user@server1 "echo connected"
The most common reason first playbooks fail is SSH authentication not working. Sort this out manually first, and you avoid a confusing UNREACHABLE error when Ansible runs.
Create Your Inventory File
The inventory file tells Ansible which servers to manage. Create a project directory and add an inventory file:
mkdir ~/ansible-project && cd ~/ansible-project
nano inventory.ini
Group servers by role for easier targeting:
[webservers]
web1 ansible_host=192.168.1.10 ansible_user=ubuntu
web2 ansible_host=192.168.1.11 ansible_user=ubuntu
[dbservers]
db1 ansible_host=192.168.1.20 ansible_user=ubuntu
[all:vars]
ansible_python_interpreter=/usr/bin/python3
Test that Ansible reaches all your hosts:
ansible all -i inventory.ini -m ping
Every host should return a green pong. If any show UNREACHABLE, fix the SSH access for that host before continuing.
Write Your First Playbook
A playbook is a YAML file that defines what Ansible should do. This one installs and starts Nginx on all web servers:
---
- name: Configure web servers
hosts: webservers
become: true
tasks:
- name: Update apt cache
apt:
update_cache: true
when: ansible_os_family == "Debian"
- name: Install Nginx
package:
name: nginx
state: present
- name: Start and enable Nginx
service:
name: nginx
state: started
enabled: true
- name: Deploy index page
copy:
content: "<h1>Managed by Ansible</h1>"
dest: /var/www/html/index.html
mode: '0644'
Run it with:
ansible-playbook -i inventory.ini webservers.yml
Tasks that make a change show as changed (yellow). Tasks that find the system already in the correct state show as ok (green). Nothing shows as an error unless something actually failed.
Understand Idempotency
Idempotency is the most important concept in Ansible. It means running the same playbook multiple times produces the same result each time. The second run does not reinstall Nginx if it is already installed. It does not overwrite a file if the content already matches.
This is what makes Ansible automation on Linux reliable in production. Run a playbook against your servers every night as a drift check. If someone manually changed a config, Ansible corrects it on the next run. Your servers stay in the state your playbook defines.
Useful Ansible Modules

These modules cover the vast majority of real-world server management tasks:
| Module | What It Does | Common Use |
|---|---|---|
apt / dnf |
Manage packages | Install, remove, update software |
service |
Manage systemd services | Start, stop, enable, disable |
copy |
Copy files to hosts | Deploy static config files |
template |
Jinja2 templates | Config files with variables |
user |
Manage user accounts | Create users, set SSH keys |
file |
Manage files and dirs | Permissions, create paths |
lineinfile |
Edit lines in files | Change one setting in a config |
command |
Run shell commands | One-offs not covered by modules |
Always prefer a dedicated module over command or shell when one exists. Dedicated modules handle idempotency correctly. Raw shell commands run every time regardless of current state.
Add Variables and Use Templates
Hard-coding values limits reuse. Variables make playbooks flexible across different environments:
---
- name: Configure app server
hosts: webservers
become: true
vars:
app_port: 3000
app_user: deploy
tasks:
- name: Create app user
user:
name: "{{ app_user }}"
state: present
- name: Open app port in firewall
ufw:
rule: allow
port: "{{ app_port }}"
proto: tcp
For config files with dynamic values, use the template module with a Jinja2 template. The Ansible Galaxy library also has thousands of pre-built roles covering common tasks like setting up databases, configuring firewalls, and deploying web servers.
Run Ad-Hoc Commands
For quick one-off tasks, Ansible runs commands directly without a playbook:
# Check disk space on all servers
ansible all -i inventory.ini -m command -a "df -h" --become
# Restart Nginx on web servers only
ansible webservers -i inventory.ini -m service -a "name=nginx state=restarted" --become
# Check kernel version across the fleet
ansible all -i inventory.ini -m command -a "uname -r"
Ad-hoc commands use the same inventory and SSH config as playbooks. They are fast for gathering information or running a quick fix without writing YAML.
Combine Ansible with Server Hardening
One of the best uses of Ansible is applying a consistent security baseline to every server you provision. Instead of running a hardening checklist manually each time, write a hardening playbook once and run it at build time. It sets SSH config, installs fail2ban, configures firewall rules, and applies sysctl settings in one shot. See our Linux server hardening checklist for the individual steps, then convert each section into an Ansible task. Also, after hardening a new server, you can deploy an Nginx reverse proxy with a single playbook run. Our guide on setting up Nginx reverse proxy on Linux covers the manual steps that translate directly into tasks.
Conclusion
Getting started with Ansible automation on Linux takes about 30 minutes. Install ansible-core, set up SSH key access, write an inventory file, and run a playbook. After that first successful run, the use cases multiply fast. Package installs, user management, config deployment, security baselines. Ansible handles all of it with the same pattern: write the desired state in YAML, run the playbook, trust the output. Start with two or three servers and one simple playbook. The rest follows naturally from there.