What Is Ansible and Why Should You Learn It?
Ansible is an open-source automation tool that lets you configure servers, deploy applications, and orchestrate complex workflows β all without installing agents on your target machines. It uses SSH and simple YAML files called playbooks to describe what you want your infrastructure to look like.
If you've ever spent hours manually configuring servers, installing packages, or deploying applications across multiple machines, Ansible will change your life. Write it once, run it everywhere, and know that every server is configured identically.
Installing Ansible
Ansible runs on a control node (your workstation or a management server) and connects to managed nodes via SSH.
On Ubuntu/Debian
sudo apt update
sudo apt install -y software-properties-common
sudo add-apt-repository --yes --update ppa:ansible/ansible
sudo apt install -y ansible
On RHEL/CentOS/Fedora
sudo dnf install -y ansible-core
Via pip (Any Platform)
pip install ansible
Verify installation:
ansible --version
# ansible [core 2.17.x]
Setting Up Your Inventory
The inventory file tells Ansible which servers to manage. Create /etc/ansible/hosts or a local inventory.ini:
[webservers]
web1.example.com
web2.example.com
192.168.1.50
[databases]
db1.example.com ansible_port=2222
db2.example.com
[all:vars]
ansible_user=deploy
ansible_python_interpreter=/usr/bin/python3
Testing Connectivity
# Ping all hosts
ansible all -i inventory.ini -m ping
# Ping specific group
ansible webservers -i inventory.ini -m ping
Ad-Hoc Commands: Quick One-Liners
Before writing playbooks, Ansible's ad-hoc commands are useful for quick tasks:
# Check disk space on all servers
ansible all -m command -a "df -h"
# Install a package on webservers
ansible webservers -m apt -a "name=nginx state=present" --become
# Restart a service
ansible webservers -m service -a "name=nginx state=restarted" --become
# Copy a file to all servers
ansible all -m copy -a "src=./config.txt dest=/etc/app/config.txt"
Your First Playbook
Playbooks are YAML files that describe the desired state of your infrastructure:
# setup-webserver.yml
---
- name: Configure Web Servers
hosts: webservers
become: yes
vars:
app_port: 8080
app_user: www-data
tasks:
- name: Update apt cache
apt:
update_cache: yes
cache_valid_time: 3600
- name: Install required packages
apt:
name:
- nginx
- certbot
- python3-certbot-nginx
- ufw
state: present
- name: Start and enable Nginx
service:
name: nginx
state: started
enabled: yes
- name: Configure firewall - allow HTTP
ufw:
rule: allow
port: "80"
proto: tcp
- name: Configure firewall - allow HTTPS
ufw:
rule: allow
port: "443"
proto: tcp
- name: Deploy Nginx configuration
template:
src: templates/nginx.conf.j2
dest: /etc/nginx/sites-available/default
notify: Restart Nginx
handlers:
- name: Restart Nginx
service:
name: nginx
state: restarted
Run the playbook:
ansible-playbook -i inventory.ini setup-webserver.yml
Using Variables and Templates
Jinja2 templates make your configurations dynamic:
# templates/nginx.conf.j2
server {
listen 80;
server_name {{ domain_name }};
location / {
proxy_pass http://127.0.0.1:{{ app_port }};
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
}
}
Variable Precedence
Variables can be defined in multiple places (lowest to highest priority):
- Inventory variables
- Group vars (
group_vars/webservers.yml) - Host vars (
host_vars/web1.yml) - Playbook vars
- Extra vars (
-e "var=value"on command line)
Organizing with Roles
Roles structure your automation into reusable components:
ansible-galaxy init roles/webserver
This creates a standard directory structure:
roles/webserver/
βββ tasks/main.yml
βββ handlers/main.yml
βββ templates/
βββ files/
βββ vars/main.yml
βββ defaults/main.yml
βββ meta/main.yml
Use roles in your playbook:
---
- name: Setup Infrastructure
hosts: all
become: yes
roles:
- common
- { role: webserver, when: "'webservers' in group_names" }
- { role: database, when: "'databases' in group_names" }
Practical Example: Full Application Deployment
# deploy-app.yml
---
- name: Deploy Application
hosts: webservers
become: yes
vars:
app_repo: "https://github.com/company/myapp.git"
app_dir: "/var/www/myapp"
app_branch: "main"
tasks:
- name: Clone/update application repository
git:
repo: "{{ app_repo }}"
dest: "{{ app_dir }}"
version: "{{ app_branch }}"
force: yes
register: git_result
- name: Install dependencies
command: npm install --production
args:
chdir: "{{ app_dir }}"
when: git_result.changed
- name: Run database migrations
command: npm run migrate
args:
chdir: "{{ app_dir }}"
when: git_result.changed
- name: Restart application
systemd:
name: myapp
state: restarted
daemon_reload: yes
when: git_result.changed
Error Handling and Conditionals
tasks:
- name: Check if config exists
stat:
path: /etc/app/config.yml
register: config_file
- name: Deploy default config
template:
src: default-config.yml.j2
dest: /etc/app/config.yml
when: not config_file.stat.exists
- name: Attempt risky operation
command: /opt/app/migrate.sh
register: migrate_result
ignore_errors: yes
- name: Rollback if migration failed
command: /opt/app/rollback.sh
when: migrate_result.failed
Ansible Vault: Managing Secrets
# Encrypt a file
ansible-vault encrypt secrets.yml
# Edit encrypted file
ansible-vault edit secrets.yml
# Run playbook with vault
ansible-playbook site.yml --ask-vault-pass
Best Practices Summary
- Idempotency β Every playbook should be safe to run multiple times
- Use roles β Organize reusable components properly
- Version control β Keep all playbooks in Git
- Test first β Use
--check(dry run) and--diffflags - Use vault β Never store passwords or keys in plain text
- Tag your tasks β Enable running specific parts of playbooks
Conclusion
Ansible is the perfect entry point into infrastructure automation. Its agentless architecture, simple YAML syntax, and massive module library make it accessible to beginners while powerful enough for enterprise environments. Start small β automate one repetitive task β and gradually build your automation toolkit from there.