Skip to main content

State Management

Unlike Terraform, Ansible does not maintain a state file. Correctness depends on idempotent task design and understanding how Ansible determines whether a change is needed.

Idempotency​

Every task must be safe to run multiple times. If the desired state is already present, the task must report changed: false and make no modifications.

# Idempotent: ansible.builtin.package checks installed state
- name: Install the nginx package
ansible.builtin.package:
name: nginx
state: present

# Not idempotent: runs every time regardless of current state
- name: Install nginx
ansible.builtin.command: apt-get install -y nginx

When ansible.builtin.command or ansible.builtin.shell is unavoidable, use creates or removes to guard execution:

- name: Extract the application archive
ansible.builtin.command:
cmd: tar -xzf /tmp/app.tar.gz -C /opt/app
creates: /opt/app/bin/app

Check Mode​

All playbooks must be compatible with --check mode. Use check_mode: false sparingly and only on tasks that are genuinely safe to execute without changes (e.g. read-only data gathering).

ansible-playbook playbooks/site.yml --check --diff

The --diff flag shows file changes in context, making check mode runs useful for code review and change approval.

Drift Detection​

Because Ansible has no native drift detection, schedule periodic runs in read-only (--check) mode via CI to surface configuration drift between deployments.

Handlers and Notify​

Use handlers to trigger service restarts or reloads only when a task actually changes state:

tasks:
- name: Deploy the nginx configuration
ansible.builtin.template:
src: nginx.conf.j2
dest: /etc/nginx/nginx.conf
notify: Restart the nginx service

handlers:
- name: Restart the nginx service
ansible.builtin.service:
name: nginx
state: restarted

Handlers run once at the end of a play, even if notified multiple times. Use ansible.builtin.meta: flush_handlers to force immediate execution when a subsequent task depends on the restarted service.

Facts and Caching​

Ansible facts describe the current state of managed hosts. Cache facts to speed up subsequent runs and reduce load on managed nodes:

# ansible.cfg
[defaults]
fact_caching = jsonfile
fact_caching_connection = .cache/facts
fact_caching_timeout = 3600

Invalidate the cache after significant host changes by deleting the cache directory or running with --flush-cache.