An Ansible playbook is a YAML file that declares the desired state of one or more groups of hosts. The playbook is structured into plays — each play targets a specific group of hosts from the inventory and contains a list of tasks to execute on those hosts. Tasks call Ansible modules, which are the actual units of work: installing a package, writing a configuration file, starting a service, creating a user, or executing a shell command. This structure gives Ansible its predictability: reading a well-written playbook, an experienced engineer can predict exactly what will happen on the target hosts without running it. The idempotency principle — the cornerstone of reliable automation — means that running the same playbook twice produces the same result, making playbooks safe to re-run after failures or configuration changes.
Handlers and tags are two Ansible features that improve operational efficiency. Handlers are tasks that run only when triggered by other tasks — the canonical use is restarting Nginx only when its configuration file changes, not on every playbook run. Without handlers, a naive automation would restart Nginx after every run even if nothing changed, causing unnecessary service interruptions. With handlers, the restart only happens when the notify directive fires, which only happens when the notifying task makes an actual change (not when it is idempotently skipping because the configuration already matches). Tags allow selective execution of specific tasks within a playbook — 'ansible-playbook deploy.yml --tags nginx' runs only the tasks tagged 'nginx', skipping everything else. This enables partial deployments without maintaining separate playbooks for each component.