Ansible cheatsheet.
Install
pipx install ansible
ansible --version
Inventory
# inventory.ini
[web]
web1.example.com
web2.example.com
[db]
db1.example.com ansible_user=postgres
[all:vars]
ansible_python_interpreter=/usr/bin/python3
YAML:
all:
hosts:
web1.example.com:
children:
web:
hosts:
web1.example.com:
web2.example.com:
db:
hosts:
db1.example.com:
ansible_user: postgres
vars:
ansible_python_interpreter: /usr/bin/python3
Ad-hoc commands
ansible all -i inventory -m ping
ansible web -i inventory -a "uptime"
ansible web -m service -a "name=nginx state=restarted" -b
ansible web -m copy -a "src=local dest=/etc/foo owner=root mode=0644" -b
-b = become root.
Playbook
- name: Configure web servers
hosts: web
become: yes
vars:
nginx_version: "1.27"
tasks:
- name: Install nginx
apt:
name: "nginx={{ nginx_version }}*"
state: present
update_cache: yes
- name: Copy config
template:
src: nginx.conf.j2
dest: /etc/nginx/nginx.conf
owner: root
mode: '0644'
notify: reload nginx
- name: Start nginx
service:
name: nginx
state: started
enabled: yes
handlers:
- name: reload nginx
service: { name: nginx, state: reloaded }
ansible-playbook -i inventory site.yml
ansible-playbook -i inventory site.yml --check # dry run
ansible-playbook -i inventory site.yml --diff # show changes
ansible-playbook -i inventory site.yml --limit web1 # one host
ansible-playbook -i inventory site.yml --tags nginx
ansible-playbook -i inventory site.yml --ask-vault-pass
Common modules
- file: { path: /tmp/foo, state: directory, mode: '0755' }
- copy: { src: a, dest: /etc/a, owner: root, mode: '0644' }
- template: { src: a.j2, dest: /etc/a }
- lineinfile: { path: /etc/foo, line: "KEY=value", regexp: "^KEY=" }
- replace: { path: /etc/foo, regexp: 'old', replace: 'new' }
- blockinfile: { path: /etc/foo, block: "..." }
- service: { name: nginx, state: restarted, enabled: yes }
- systemd: { name: nginx, state: started, daemon_reload: yes }
- apt: { name: ["nginx", "curl"], state: present }
- dnf: { name: nginx, state: latest }
- yum: { name: nginx, state: present }
- user: { name: alice, groups: [sudo,docker], append: yes }
- group: { name: devs }
- git: { repo: url, dest: /opt/app, version: main }
- cron: { name: backup, minute: 0, hour: 2, job: /opt/backup.sh }
- mount: { path: /mnt, src: /dev/sdb1, fstype: ext4, state: mounted }
- ufw: { rule: allow, port: 80 }
- shell: "command"
- command: "command" # no shell
Conditionals
- service: { name: nginx, state: started }
when: ansible_os_family == "Debian"
- name: install packages
apt: { name: "{{ item }}" }
loop:
- nginx
- curl
when: ansible_distribution_major_version | int >= 20
Variables
vars:
port: 8000
vars_files:
- vars/main.yml
group_vars/web.yml and host_vars/web1.example.com.yml auto-loaded.
Templates (Jinja2)
# templates/nginx.conf.j2
worker_processes {{ ansible_processor_vcpus }};
server {
listen {{ port | default(80) }};
server_name {{ inventory_hostname }};
}
Roles
roles/
└── nginx/
├── tasks/main.yml
├── handlers/main.yml
├── templates/
├── files/
├── vars/main.yml
├── defaults/main.yml
└── meta/main.yml
- hosts: web
roles:
- nginx
- { role: db, when: install_db }
ansible-galaxy
ansible-galaxy init roles/myrole
ansible-galaxy install geerlingguy.nginx
ansible-galaxy collection install community.general
Vault (secrets)
ansible-vault create secrets.yml
ansible-vault edit secrets.yml
ansible-vault view secrets.yml
ansible-vault encrypt vars/secrets.yml
ansible-vault decrypt vars/secrets.yml
ansible-playbook -i inv site.yml --ask-vault-pass
ansible-playbook -i inv site.yml --vault-password-file ~/.vault-pass
Facts
- debug: var=ansible_facts
- debug: msg="OS: {{ ansible_distribution }} {{ ansible_distribution_version }}"
ansible-inventory --host hostname --list shows facts.
Skip gathering:
gather_facts: no
Saves time when not needed.
Loops
- apt: { name: "{{ item }}" }
loop: [nginx, curl, git]
- user: { name: "{{ item.name }}", groups: "{{ item.groups }}" }
loop:
- { name: alice, groups: [sudo] }
- { name: bob, groups: [devs] }
Handlers
- name: change config
template: { src: a.j2, dest: /etc/a }
notify: restart service
handlers:
- name: restart service
service: { name: svc, state: restarted }
Runs once at end if notified.
Idempotency
Modules should be idempotent: same playbook twice → no change second time. Avoid shell / command unless necessary, or use creates: / removes:.
- command: "make build"
args:
creates: /opt/app/build
Dynamic inventory
ansible-inventory -i aws_ec2.yml --list
Plugin pulls from AWS, GCP, Azure, etc.
Common mistakes
shelleverywhere — not idempotent.- No
become: yesfor system tasks. - Hardcoding hosts in playbook (use inventory).
- Mixing
commandandshellsemantics. - Forgetting
notify→ service not restarted.
Read this next
If you want my Ansible starter (web + db roles), it’s at rajpoot.dev .
Building something AI-, backend-, or data-heavy and want a second pair of eyes? I do consulting and freelance work — see my projects and ways to reach me at rajpoot.dev .