Introduction

Ansible automates server management: installing software, deploying configs, managing services, and more — all without agents. You write what you want in YAML, and Ansible makes it happen over SSH. This guide takes you from zero to your first working playbook.

What Is Ansible?

Ansible is an automation tool that:

  • Connects to servers via SSH (no agent needed)
  • Reads your instructions from YAML files (playbooks)
  • Executes modules on remote servers to achieve desired state
  • Reports what changed and what was already correct
Your laptop          SSH           Remote servers
┌──────────┐    ──────────>    ┌──────────────┐
│ Ansible  │                   │ web-server-1 │
│ Playbook │    ──────────>    │ web-server-2 │
│          │                   │ db-server-1  │
└──────────┘    ──────────>    └──────────────┘
  Control node                   Managed nodes

Install Ansible

# macOS
brew install ansible

# Ubuntu/Debian
sudo apt update
sudo apt install ansible

# RHEL/Fedora
sudo dnf install ansible

# pip (any OS — recommended for latest version)
pip install ansible

# Verify installation
ansible --version
# ansible [core 2.20.0]

Your First Command (Ad-Hoc)

# Ping a server (test connection)
ansible all -i "server1.example.com," -m ping
# Note: comma after hostname is required for single-host inventory

# Run a command
ansible all -i "server1.example.com," -m command -a "uptime"

# Install a package
ansible all -i "server1.example.com," -m apt -a "name=nginx state=present" --become

Inventory — Define Your Servers

# inventory.yml
all:
  children:
    webservers:
      hosts:
        web1:
          ansible_host: 192.168.1.10
        web2:
          ansible_host: 192.168.1.11
    dbservers:
      hosts:
        db1:
          ansible_host: 192.168.1.20
  vars:
    ansible_user: deploy
    ansible_ssh_private_key_file: ~/.ssh/id_ed25519
# List hosts in inventory
ansible-inventory -i inventory.yml --list

# Ping all webservers
ansible webservers -i inventory.yml -m ping

Your First Playbook

# setup-webserver.yml
---
- name: Set up web servers
  hosts: webservers
  become: true  # Run as root

  vars:
    app_port: 8080
    packages:
      - nginx
      - python3
      - curl

  tasks:
    - name: Update apt cache
      ansible.builtin.apt:
        update_cache: true
        cache_valid_time: 3600

    - name: Install required packages
      ansible.builtin.apt:
        name: "{{ packages }}"
        state: present

    - name: Deploy nginx configuration
      ansible.builtin.template:
        src: templates/nginx.conf.j2
        dest: /etc/nginx/sites-available/default
      notify: Restart nginx

    - name: Ensure nginx is running
      ansible.builtin.service:
        name: nginx
        state: started
        enabled: true

    - name: Verify nginx is responding
      ansible.builtin.uri:
        url: "http://{{ inventory_hostname }}/index.html"
      register: result
      until: result.status == 200
      retries: 5
      delay: 2

  handlers:
    - name: Restart nginx
      ansible.builtin.service:
        name: nginx
        state: restarted
# Run the playbook
ansible-playbook -i inventory.yml setup-webserver.yml

# Dry run first (preview changes)
ansible-playbook -i inventory.yml setup-webserver.yml --check --diff

Key Concepts

Modules — What Ansible Can Do

# File management
- ansible.builtin.file:         # Create/delete files and directories
- ansible.builtin.copy:         # Copy files to remote
- ansible.builtin.template:     # Copy with variable substitution

# Package management
- ansible.builtin.apt:          # Debian/Ubuntu packages
- ansible.builtin.dnf:          # RHEL/Fedora packages

# Service management
- ansible.builtin.service:      # Start/stop/restart services
- ansible.builtin.systemd:      # Systemd-specific operations

# User management
- ansible.builtin.user:         # Create/modify users
- ansible.builtin.group:        # Create/modify groups

# Commands
- ansible.builtin.command:      # Run commands (safe)
- ansible.builtin.shell:        # Run commands (with pipes/redirects)

Variables

# In playbook
vars:
  app_name: myapp

# In separate file
vars_files:
  - vars/common.yml

# From command line
# ansible-playbook site.yml -e "app_name=myapp"

# Use in tasks
- name: Deploy {{ app_name }}
  ansible.builtin.copy:
    src: "{{ app_name }}.conf"
    dest: "/etc/{{ app_name }}/config.yml"

Conditionals

# Run task only when condition is true
- name: Install on Ubuntu only
  ansible.builtin.apt:
    name: nginx
  when: ansible_distribution == "Ubuntu"

- name: Restart if config changed
  ansible.builtin.service:
    name: myapp
    state: restarted
  when: config_result is changed

Loops

# Iterate over a list
- name: Create users
  ansible.builtin.user:
    name: "{{ item }}"
    state: present
  loop:
    - alice
    - bob
    - charlie

Handlers — React to Changes

tasks:
  - name: Update config
    ansible.builtin.template:
      src: app.conf.j2
      dest: /etc/app/config.yml
    notify: Restart app
    # Handler only runs if this task makes a change

handlers:
  - name: Restart app
    ansible.builtin.service:
      name: myapp
      state: restarted

Roles — Reusable Automation

# Create a role skeleton
ansible-galaxy role init roles/webserver
# Use roles in a playbook
- hosts: webservers
  roles:
    - common
    - nginx
    - role: app_deploy
      vars:
        app_version: "2.0"

Common First-Time Issues

# Problem: "Permission denied"
# Fix: Add --become (or become: true in playbook)
ansible-playbook site.yml --become

# Problem: "Host key verification failed"
# Fix: Add to ansible.cfg
[defaults]
host_key_checking = false

# Problem: "No hosts matched"
# Fix: Check inventory file and host patterns
ansible-inventory -i inventory.yml --graph

# Problem: "MODULE FAILURE" on command
# Fix: Use FQCN and check module parameters
# Bad:  apt: pkg=nginx
# Good: ansible.builtin.apt: name=nginx state=present

Next Steps

  1. Learn roles — Package your playbooks into reusable components
  2. Use Ansible Vault — Encrypt passwords and secrets
  3. Set up inventory per environment — staging, production
  4. Add to CI/CD — Automate deployments on git push
  5. Explore collections — Community modules for AWS, Docker, Kubernetes
  1. Ansible Playbook Guide
  2. Ansible Inventory Guide
  3. Ansible Roles Guide
  4. Ansible Variables Guide
  5. Ansible Vault Guide
  6. Ansible Best Practices

Conclusion

Ansible uses SSH to connect, YAML to describe desired state, and modules to make it happen. Start with ad-hoc commands to test connectivity, then write playbooks for repeatable automation. Use --check --diff to preview before applying. As your project grows, organize with roles and manage secrets with Vault. The learning curve is gentle — if you can write YAML, you can write Ansible.