Ansible automates server configuration through simple, human-readable YAML files — letting you define your server's desired state once and apply it consistently across one or many servers, without manual SSH sessions.
Why Use Ansible
- Reproducible server configuration — no more "it worked on the old server but not the new one"
- Version-controlled infrastructure, reviewable like application code
- Apply the same configuration across multiple servers consistently
- Agentless — only requires SSH access, no software installed on managed servers
Prerequisites
- A control machine (your local machine or a dedicated management VPS) with Ansible installed
- SSH key access to the servers you want to manage
Step 1 — Install Ansible on the Control Machine
sudo apt install ansible -y
Step 2 — Create an Inventory File
mkdir ~/ansible-project && cd ~/ansible-project
nano inventory.ini
[webservers]
web1 ansible_host=203.0.113.10 ansible_user=deploy
web2 ansible_host=203.0.113.11 ansible_user=deploy
[databases]
db1 ansible_host=203.0.113.12 ansible_user=deploy
Step 3 — Test Connectivity
ansible all -i inventory.ini -m ping
Step 4 — Run an Ad-Hoc Command
ansible webservers -i inventory.ini -a "df -h" -b
-b (become) runs the command with sudo privileges.
Step 5 — Create Your First Playbook
nano setup-webserver.yml
---
- name: Configure web servers
hosts: webservers
become: true
tasks:
- name: Update apt cache
apt:
update_cache: yes
- name: Install Nginx
apt:
name: nginx
state: present
- name: Ensure Nginx is running and enabled
systemd:
name: nginx
state: started
enabled: true
- name: Allow HTTP and HTTPS through UFW
ufw:
rule: allow
port: "{{ item }}"
loop:
- "80"
- "443"
Step 6 — Run the Playbook
ansible-playbook -i inventory.ini setup-webserver.yml
Step 7 — Verify Idempotency
Run the playbook again — Ansible should report no changes needed the second time, since the desired state is already achieved. This idempotency is a core Ansible principle: running a playbook repeatedly should be safe and produce the same end result.
Using Variables
vars:
app_port: 3000
tasks:
- name: Deploy application
template:
src: app.service.j2
dest: /etc/systemd/system/app.service
Organizing with Roles (For Larger Projects)
ansible-galaxy init roles/webserver
Roles let you organize related tasks, templates, and variables into reusable, shareable units — useful once your playbooks grow beyond a single file.
Example: Applying Server Hardening via Ansible
tasks:
- name: Disable root SSH login
lineinfile:
path: /etc/ssh/sshd_config
regexp: '^PermitRootLogin'
line: 'PermitRootLogin no'
notify: restart sshd
handlers:
- name: restart sshd
systemd:
name: sshd
state: restarted
Common Errors
"UNREACHABLE" error — verify SSH connectivity manually first: ssh [email protected]; Ansible relies entirely on working SSH access.
Permission errors on tasks requiring sudo — ensure become: true is set at the playbook or task level.
Best Practices
- Keep playbooks version-controlled alongside your application code
- Design tasks to be idempotent — safe to run repeatedly
- Use roles to organize configuration as your infrastructure grows
Related Articles
- How to Version Control Your Server Configuration
- How to Harden a Fresh Linux VPS in 15 Minutes
- Infrastructure as Code Basics: Managing VPS Config with Terraform
