LinuxXXXIV · Configuration ManagementDesired state
Desired state and idempotency - the foundations of CM
What you'll learn
- Define desired state vs imperative commands
- Explain why idempotency matters
- Recognise idempotent vs non-idempotent operations
- Write idempotent CM code
Prerequisites
Verified against Ubuntu 24.04 LTS · Debian 12 (Bookworm) · RHEL 9.x · Rocky Linux 9.x · AlmaLinux 9.x · Linux kernel 6.1 LTS / 6.6 LTS · systemd 255+ · OpenSSH 8.7p1 (RHEL 9) / 9.6p1 (Ubuntu 24.04) · nftables 1.0.x · chrony 4.x · Pacemaker 2.1.x · Corosync 3.1.x · 2026-08-09
Configuration management works by declaring desired state (“the file should contain X”) and letting the tool enforce it. Idempotency means applying the same configuration twice is safe.
Imperative vs declarative
Imperative (“do this”):
apt install nginx
systemctl start nginx
echo "worker_processes 4;" > /etc/nginx/nginx.conf
systemctl reload nginx
If you run this twice, the result depends on the starting
state. If nginx is already installed, fine. If the config
file was different, it is overwritten.
Declarative (“the state should be”):
# Ansible
- apt:
name: nginx
state: present
- service:
name: nginx
state: started
- copy:
content: |
worker_processes 4;
dest: /etc/nginx/nginx.conf
- service:
name: nginx
state: reloaded
If you run this twice, the result is the same: nginx is installed, running, with the right config. Running the playbook is idempotent.
Why idempotency matters
- Safe to re-run: if the playbook crashes halfway, you can re-run it without breaking anything.
- Drift correction: a drifted host can be brought back to desired state by re-running the playbook.
- Verification: idempotent operations can be checked after-the-fact; the state is known to be correct.
Non-idempotent operations (e.g. command: rm -rf /tmp/foo)
can be destructive if run twice or in the wrong order.
Idempotency patterns
| Idempotent | Non-idempotent |
|---|---|
apt: name=X state=present | command: apt install -y X |
file: state=touch | command: touch X |
lineinfile: state=present | command: echo X >> file |
service: state=started enabled=yes | command: systemctl start X |
user: state=present | command: useradd X |
Ansible modules are designed to be idempotent. Shell commands are not. Prefer modules.
Avoid common pitfalls
- Always run a playbook twice to verify it is idempotent.
- Never use
commandfor things Ansible has a module for. - Never use shell that depends on the order of operations.
- Always read the diff before applying in production.
Desired state examples
# Package state
- apt:
name:
- nginx
- postgresql-15
state: present
# Service state
- service:
name: nginx
state: started
enabled: yes
# File state
- copy:
src: files/nginx.conf
dest: /etc/nginx/nginx.conf
owner: root
group: root
mode: '0644'
# User state
- user:
name: deploy
state: present
shell: /bin/bash
Each declares what should be true. Ansible figures out how to make it true.
Knowledge check
Knowledge check · 3 questions
Q1. What is the difference between imperative and declarative CM?
Q2. Running an idempotent playbook twice produces the same result.
Q3. Which of the following are idempotent? Select all that apply.
Passing score: 75%. Answers are checked in this browser.