Ansible is an agentless automation tool that uses SSH to configure servers, deploy applications, and orchestrate workflows. No daemons, no agents — just Python and SSH.
Why Ansible?
- Agentless — nothing to install on managed nodes
- Idempotent — running a playbook twice produces the same result
- Declarative — describe the desired state, not the steps
- Simple — YAML playbooks are readable by anyone
Installation
# macOS
brew install ansible
# Ubuntu / Debian
sudo apt update
sudo apt install ansible
# Verify
ansible --version
Inventory
The inventory defines which servers Ansible manages. Create an inventory.ini:
[webservers]
web1.example.com ansible_user=deploy
web2.example.com ansible_user=deploy
[databases]
db1.example.com ansible_user=deploy
[all:vars]
ansible_python_interpreter=/usr/bin/python3
Test connectivity:
ansible all -i inventory.ini -m ping
Your First Playbook
Create setup-webserver.yml:
---
- name: Configure web servers
hosts: webservers
become: yes
vars:
app_port: 3000
node_version: "20"
tasks:
- name: Update apt cache
apt:
update_cache: yes
cache_valid_time: 3600
- name: Install required packages
apt:
name:
- nginx
- curl
- git
- ufw
state: present
- name: Enable UFW firewall
ufw:
state: enabled
policy: deny
- name: Allow SSH
ufw:
rule: allow
name: OpenSSH
- name: Allow HTTP and HTTPS
ufw:
rule: allow
port: "{{ item }}"
proto: tcp
loop:
- "80"
- "443"
- name: Copy Nginx config
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 it:
ansible-playbook -i inventory.ini setup-webserver.yml
Roles
Roles organize playbooks into reusable components:
roles/
nginx/
tasks/main.yml
templates/nginx.conf.j2
handlers/main.yml
defaults/main.yml
nodejs/
tasks/main.yml
defaults/main.yml
postgresql/
tasks/main.yml
templates/pg_hba.conf.j2
handlers/main.yml
Use roles in a playbook:
---
- name: Full server setup
hosts: webservers
become: yes
roles:
- nginx
- nodejs
Common Modules
| Module | Purpose | Example |
|---|---|---|
apt / yum | Package management | apt: name=nginx state=present |
copy | Copy files | copy: src=app.conf dest=/etc/app.conf |
template | Jinja2 templates | template: src=nginx.j2 dest=/etc/nginx/nginx.conf |
service | Manage services | service: name=nginx state=started enabled=yes |
user | Manage users | user: name=deploy shell=/bin/bash |
file | File/directory ops | file: path=/data state=directory mode=0755 |
git | Clone repositories | git: repo=https://github.com/... dest=/opt/app |
docker_container | Manage containers | docker_container: name=app image=myapp:latest |
Ansible Vault
Encrypt sensitive variables:
# Create encrypted file
ansible-vault create secrets.yml
# Edit encrypted file
ansible-vault edit secrets.yml
# Run playbook with vault
ansible-playbook -i inventory.ini site.yml --ask-vault-pass
Best Practices
- Use
--checkmode (dry run) before applying changes - Group variables in
group_vars/and host-specific ones inhost_vars/ - Always use
become: yesexplicitly rather than running Ansible as root - Tag tasks so you can run subsets:
ansible-playbook site.yml --tags "nginx" - Test playbooks with Molecule for role unit testing
Ansible makes server configuration repeatable and version-controlled. Combined with Git, it brings the same rigor to infrastructure that developers have for application code.
Enjoyed this article?
Get more DevOps insights delivered to your inbox.
Get new posts by email
Subscribe to get an email when a new blog post is published. Skip anytime.
No spam, unsubscribe anytime.
Related Posts
Discussion
0 comments
Sign in to join the conversation.
Be the first to comment
Start a conversation about this post
