Stackbook Logo
← Back to Tools

Ansible Playbooks Guide

A practical introduction to Ansible: inventories, playbooks, roles, handlers and vault, with real commands for configuring servers without agents.

4 min read

What it is and when to use it

Ansible is an agentless automation tool for configuration management, application deployment and orchestration. It connects over SSH (or WinRM for Windows), runs small programs called modules, and removes them afterwards. Playbooks are written in YAML, so they are readable by people who do not write code.

Use Ansible to configure virtual machines and bare metal, patch fleets, bootstrap hosts after Terraform creates them, and run repeatable operational tasks. For immutable container workloads it is less central, but it remains useful for building images and managing the machines underneath.

Quick start

Install with pip or your package manager, then create an inventory file.

[web]
web1.example.com
web2.example.com

[db]
db1.example.com

[all:vars]
ansible_user=deploy

Test connectivity with an ad hoc command.

ansible all -i inventory.ini -m ping
ansible web -i inventory.ini -b -m apt -a "name=nginx state=present"

Now write a first playbook, site.yml.

---
- name: Configure web servers
  hosts: web
  become: true
  vars:
    http_port: 80
  tasks:
    - name: Install nginx
      ansible.builtin.apt:
        name: nginx
        state: present
        update_cache: true

    - name: Deploy site config
      ansible.builtin.template:
        src: site.conf.j2
        dest: /etc/nginx/conf.d/site.conf
        mode: "0644"
      notify: Reload nginx

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

  handlers:
    - name: Reload nginx
      ansible.builtin.service:
        name: nginx
        state: reloaded

Run it, first as a dry run.

ansible-playbook -i inventory.ini site.yml --check --diff
ansible-playbook -i inventory.ini site.yml

Core concepts

Inventory lists hosts and groups. For cloud environments use a dynamic inventory plugin, for example amazon.aws.aws_ec2, so hosts are discovered by tags.

Modules do the work. Prefer purpose-built modules such as apt, copy, user, and systemd over the shell or command modules, because they are idempotent: running twice changes nothing the second time.

Tasks, handlers and notify. A handler runs once at the end of a play, and only if a task reported a change. This is the standard pattern for restarting a service after a config update.

Variables and templates. Variables come from inventory, group_vars, host_vars, play vars and the command line. Jinja2 templates render files from them.

Roles package tasks, templates, defaults and handlers into a reusable directory structure.

ansible-galaxy init roles/webserver
ansible-galaxy collection install community.general
- hosts: web
  become: true
  roles:
    - role: webserver
      vars:
        http_port: 8080

Vault encrypts secrets so they can live in Git.

ansible-vault create group_vars/all/vault.yml
ansible-playbook site.yml --ask-vault-pass

Tags and limits let you run part of a playbook, for example the --tags config option or --limit web1.example.com to touch one host.

Pitfalls

  • Overusing shell and command tasks breaks idempotency. When you must, add creates, removes or changed_when conditions.
  • Forgetting become: true leads to permission errors on system paths.
  • YAML quoting traps: values starting with a brace or containing a colon followed by a space need quotes, and file modes should be quoted strings such as "0644".
  • Variable precedence is complex. Keep defaults in role defaults, and override in group_vars rather than scattering definitions.
  • Running playbooks against production without --check and --limit first invites outages. Use serial to roll changes through a fleet in batches.
  • Use fully qualified collection names like ansible.builtin.copy to avoid ambiguity.
  • Store secrets in Vault or an external store, never in plain variables files.

How it fits an architecture

A common layering is Terraform for provisioning, Ansible for configuration. Terraform creates instances and networking, then outputs addresses or tags; Ansible reads them through a dynamic inventory and installs packages, hardens SSH, deploys agents and application releases. In a pipeline, GitHub Actions can call ansible-playbook on merge, with Vault passwords held in encrypted secrets. In container-first shops Ansible often shrinks to host baselines, node patching and image builds, while Helm and Argo CD manage workloads.

Key takeaways

  • Ansible is agentless and uses SSH, so adoption is quick.
  • Idempotent modules plus handlers give safe, repeatable configuration.
  • Organize reuse with roles and collections, and keep variables in group_vars.
  • Always dry run with check and diff, and limit scope before touching production.
  • Encrypt secrets with Vault and roll changes in batches using serial.