If you’ve ever found yourself manually configuring servers, installing packages, or deploying applications across multiple machines, you know how tedious and error-prone this process can be. Enter Ansible – a powerful automation tool that can transform your infrastructure management from a manual chore into an elegant, repeatable process.
What is Ansible?
Ansible is an open-source automation platform that simplifies complex tasks such as configuration management, application deployment, and orchestration. Unlike other automation tools, Ansible is agentless, which means you don’t need to install any software on the machines you want to manage. Furthermore, it uses SSH for Linux/Unix systems and WinRM for Windows, making it lightweight and easy to adopt. As a result, teams can implement automation quickly and efficiently.
Why Choose Ansible?
Simple and Human-Readable: Ansible uses YAML syntax, which reads almost like plain English. No complex programming knowledge required.
Agentless Architecture: No need to install agents on target machines – just SSH access is enough.
Idempotent Operations: Run the same playbook multiple times safely. Ansible only makes changes when necessary.
Extensive Module Library: Over 3,000+ modules covering everything from cloud providers to network devices.
Installing Ansible
Let’s get Ansible installed on your control machine (the computer you’ll run Ansible from).
On Ubuntu/Debian:
sudo apt update
sudo apt install ansible
CentOS/RHEL:
sudo yum install epel-release
sudo yum install ansible
macOS:
brew install ansible
Using pip (any OS):
pip install ansible
Verify your installation:
ansible --version
Key Concepts
Before diving into our first deployment, let’s understand some core concepts:
Inventory: A file that defines the hosts and groups of hosts you want to manage.
Playbooks: YAML files containing a series of tasks to execute on your hosts.
Tasks: Individual actions like installing packages, copying files, or starting services.
Modules: Pre-built code that performs specific tasks (like apt package management or copy file operations).
Roles: Reusable collections of tasks, files, templates, and variables.
Setting Up Your First Project
Let’s create a simple project structure:
mkdir ansible-tutorial
cd ansible-tutorial
mkdir -p group_vars host_vars roles
touch inventory.ini ansible.cfg site.yml
Your directory should look like this:
ansible-tutorial/
├── ansible.cfg
├── group_vars/
├── host_vars/
├── inventory.ini
├── roles/
└── site.yml
Creating Your Inventory
The inventory file tells Ansible which servers to manage. Create a simple inventory.ini:
[webservers]
web1 ansible_host=192.168.1.100 ansible_user=ubuntu
web2 ansible_host=192.168.1.101 ansible_user=ubuntu
#webservers databases
[databases]
db1 ansible_host=192.168.1.200 ansible_user=ubuntu
[production:children]
This inventory defines:
- Two web servers in the
webserversgroup - One database server in the
databasesgroup - A parent group
productioncontaining both groups
Your First Playbook
Now let’s create a playbook to deploy a simple web application. Edit site.yml:
---
- name: Deploy Simple Web Application
hosts: webservers
become: yes
vars:
app_name: "my-web-app"
app_port: 8080
tasks:
- name: Update package cache
apt:
update_cache: yes
cache_valid_time: 3600
- name: Install required packages
apt:
name:
- nginx
- python3
- python3-pip
- git
state: present
- name: Create application directory
file:
path: "/opt/{{ app_name }}"
state: directory
owner: www-data
group: www-data
mode: '0755'
- name: Clone application repository
git:
repo: "https://github.com/your-username/simple-flask-app.git"
dest: "/opt/{{ app_name }}"
version: main
notify: restart application
- name: Install Python dependencies
pip:
requirements: "/opt/{{ app_name }}/requirements.txt"
executable: pip3
- name: Create systemd service file
template:
src: app.service.j2
dest: "/etc/systemd/system/{{ app_name }}.service"
notify: restart application
- name: Configure Nginx
template:
src: nginx.conf.j2
dest: "/etc/nginx/sites-available/{{ app_name }}"
notify: restart nginx
- name: Enable Nginx site
file:
src: "/etc/nginx/sites-available/{{ app_name }}"
dest: "/etc/nginx/sites-enabled/{{ app_name }}"
state: link
notify: restart nginx
- name: Start and enable services
systemd:
name: "{{ item }}"
state: started
enabled: yes
daemon_reload: yes
loop:
- "{{ app_name }}"
- nginx
handlers:
- name: restart application
systemd:
name: "{{ app_name }}"
state: restarted
- name: restart nginx
systemd:
name: nginx
state: restarted
Creating Templates
Ansible uses Jinja2 templates to create dynamic configuration files. Create a templates directory and add these files:
templates/app.service.j2
[Unit]
Description={{ app_name }} Web Application
After=network.target
[Service]
User=www-data
Group=www-data
WorkingDirectory=/opt/{{ app_name }}
ExecStart=/usr/bin/python3 app.py
Restart=always
RestartSec=3
[Install]
WantedBy=multi-user.target
templates/nginx.conf.j2
server {
listen 80;
server_name {{ ansible_host }};
location / {
proxy_pass http://127.0.0.1:{{ app_port }};
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}
}
Configuration File
Create an ansible.cfg file to set some defaults:
[defaults]
inventory = inventory.ini
host_key_checking = False
retry_files_enabled = False
stdout_callback = yaml
[ssh_connection]
pipelining = True
Running Your First Deployment
Before running the full playbook, test connectivity to your hosts:
ansible all -m ping
If that works, you can run your playbook:
ansible-playbook site.yml
For a dry run to see what would change without actually making changes:
ansible-playbook site.yml --check
To run only specific tasks with tags:
ansible-playbook site.yml --tags "packages"
Advanced Tips
Using Vault for Secrets
Never store passwords or API keys in plain text. Use Ansible Vault:
ansible-vault create group_vars/all/vault.yml
Organizing with Roles
For larger projects, organize your tasks into roles:
ansible-galaxy init roles/webserver
This creates a structured role directory with tasks, handlers, templates, and variables.
Testing Your Playbooks
Consider using tools like Molecule to test your playbooks:
pip install molecule[docker]
molecule init scenario --driver-name docker
Troubleshooting Common Issues
SSH Connection Issues: Ensure SSH keys are set up or use --ask-pass the flag.
Permission Denied: Use --ask-become-pass for sudo password or configure passwordless sudo.
Module Not Found: Check if the required Python modules are installed on target hosts.
Idempotency Issues: Always use appropriate modules and parameters to ensure tasks are idempotent.
Next Steps
Now that you’ve completed your first automated deployment, consider exploring:
- Dynamic Inventories: Pull inventory from cloud providers
- Ansible Galaxy: Use community roles and collections
- AWX/Tower: Web-based UI for Ansible automation
- Integration: Connect Ansible with CI/CD pipelines
- Custom Modules: Write your own modules for specific needs
Conclusion
Ansible transforms infrastructure management from a manual, error-prone process into reliable, repeatable automation. With just YAML and SSH, you can manage everything from a single server to thousands of machines across multiple cloud providers.
Start small, automate one task at a time, and gradually build more complex playbooks. Before you know it, you’ll wonder how you ever managed infrastructure without Ansible.

