⌨️ Daily Life Commands

Updated 18 August 2026

Daily Life Commands

Every command marked ⭐ across Modules 01–14, collected into one working reference — organised by what you are trying to do, not by which module taught it.

If you only memorise one section, make it 🚦 Before You Run Anything.


🚦 Before you run anything

The pre-flight sequence. Four commands, none of which change anything, run before any unfamiliar playbook touches production.
bash
ansible-playbook site.yml --syntax-check                    # does it parse?
ansible-playbook site.yml --list-hosts                      # what is the blast radius?
ansible-playbook site.yml --list-tasks                      # what will it do, in order?
ansible-playbook site.yml --check --diff --limit web01      # what WOULD change?
bash
# And when the pattern matters more than the playbook
ansible 'prod:&web:!web01' --list-hosts                     # verify BEFORE anything destructive

🔎 Orientation — an unfamiliar repository or host

bash
ansible --version                              # version AND which ansible.cfg won
ansible-config dump --only-changed             # every non-default setting, with its source
ansible-galaxy collection list                 # what collections are installed
ansible-galaxy install -r requirements.yml     # get the pinned dependencies
ansible-inventory -i inventory/ --graph        # the group tree
ansible-inventory -i inventory/ --graph --vars # tree plus variables
ansible-inventory -i inventory/ --host web01   # everything resolved for one host
ansible-playbook site.yml --list-tasks         # the full ordered scope
The five-command opening move on any incident: ansible --version, ansible-inventory --graph, --list-tasks, ansible <host> -m ping -vvv, and git log --since="<when it last worked>". Between them they cover configuration, inventory, scope, connectivity and recent change — which is where the cause almost always is.

🔌 Connectivity

bash
ansible all -m ansible.builtin.ping                        # SSH + auth + Python, all at once
ansible all -m ansible.builtin.ping -o                     # one line per host
ansible web01 -m ansible.builtin.ping -vvv                 # the exact ssh command built
ansible web01 -m ping -vvv 2>&1 | grep -o 'IdentityFile=[^]*'   # WHICH key was offered?
ansible web01 -m ansible.builtin.command -a "id"           # who am I after login?
ansible web01 -m ansible.builtin.command -a "id" -b        # who am I after become?

When it fails

bash
ssh -vvv [email protected]                                  # ALWAYS reproduce outside Ansible
ssh web01 'ls -ld ~ ~/.ssh ~/.ssh/authorized_keys'         # target-side permissions
ssh web01 'sudo journalctl -u sshd -n 50'                  # THE REAL REASON for auth failures
ssh-keygen -R web01                                        # clear a changed host key
ssh-keyscan -H web01 >> ~/.ssh/known_hosts
ssh-keygen -t ed25519 -f ~/.ssh/ansible_ed25519
ssh-copy-id -i ~/.ssh/ansible_ed25519.pub [email protected]
ssh-add ~/.ssh/ansible_ed25519                             # load a passphrase-protected key
sshd never tells the client why it refused — that would leak information to an attacker. Permission denied (publickey) is deliberately uninformative, and the real reason is only in the target's own journalctl -u sshd, usually bad ownership or modes for directory.

And check whether someone ran it under sudo — that changes $HOME to /root, so Ansible reads /root/.ssh and your key is never offered.


🏃 Running playbooks

bash
ansible-playbook site.yml
ansible-playbook site.yml --limit web01                     # one host first, always
ansible-playbook site.yml --limit @site.retry               # only the hosts that failed
ansible-playbook site.yml --check --diff                    # the closest thing to terraform plan
ansible-playbook site.yml --tags deploy                     # run one slice
ansible-playbook site.yml --skip-tags slow
ansible-playbook site.yml --list-tags                       # what tags exist?
ansible-playbook site.yml --start-at-task "Deploy config"   # resume after a fix
ansible-playbook site.yml --step                            # confirm each task
ansible-playbook site.yml -e app_version=2.4.1              # extra vars always win
ansible-playbook site.yml -e '{"debug": false}'             # JSON preserves real types
ansible-playbook site.yml -e "@vars/production.yml"         # load a whole file
ansible-playbook site.yml --flush-cache                     # discard cached facts
ansible-playbook site.yml -f 50                             # forks for this run
ansible-playbook site.yml -vvv                              # connection-level debugging

Multi-environment

bash
ansible-playbook -i inventories/staging    playbooks/site.yml
ansible-playbook -i inventories/production playbooks/site.yml

⚡ Ad-hoc operations

bash
ansible all -m ansible.builtin.setup                                     # every fact
ansible all -m ansible.builtin.setup -a 'filter=ansible_distribution*'   # filtered
ansible all -m ansible.builtin.setup -a 'filter=ansible_local'           # custom facts
ansible all -m ansible.builtin.shell -a "uptime" -o                      # quick fleet check
ansible all -m ansible.builtin.shell -a "df -h /" -o                     # who is out of disk?
ansible web -m ansible.builtin.service -a "name=nginx state=restarted" -b
ansible all -m ansible.builtin.package -a "name=htop state=present" -b --check -o   # AUDIT
That last one is the underused pattern. --check works on ad-hoc commands, which turns "which of my 200 hosts is missing the monitoring agent?" into one command with no playbook at all.

🔍 Debugging

The three-step variable diagnosis

yaml
- ansible.builtin.debug: var=hostvars[inventory_hostname]   # 1. what DOES this host have?
- ansible.builtin.debug: var=myvar                          # 2. what is the value?
- ansible.builtin.debug: msg="{{ myvar | type_debug }}"     # 3. what TYPE is it?
Step 3 resolves more cases than people expect. A string "false" that is truthy, a string "80" compared numerically, a lazy generator where a list was expected — all look correct in the YAML and all behave wrongly.

Other inspection

yaml
- ansible.builtin.debug: var=result                    # a full registered object
- ansible.builtin.debug: var=ansible_facts             # all gathered facts
- ansible.builtin.debug: var=groups                    # the whole inventory
- ansible.builtin.debug: var=group_names               # groups THIS host is in

- ansible.builtin.assert:                              # fail early, fail clearly
    that: [app_port | int > 1024]
    fail_msg: "app_port={{ app_port }} type={{ app_port | type_debug }}"

The interactive debugger

yaml
strategy: debug              # play level - debugger on any failure
debugger: on_failed          # task level
plain text
(debug) p task.args                       # arguments AFTER templating
(debug) p task_vars['myvar']
(debug) p result._result                  # the full failure object
(debug) task.args['_raw_params'] = '...'  # modify in place
(debug) r                                 # REDO just this task
(debug) c                                 # continue

Logging and timing

bash
ANSIBLE_LOG_PATH=/tmp/run.log ansible-playbook site.yml
grep -n 'FAILED\|UNREACHABLE\|rescued' /tmp/run.log
ANSIBLE_CALLBACKS_ENABLED=profile_tasks,timer ansible-playbook site.yml   # where time goes

"What changed?"

bash
git log --since="last Tuesday" --oneline -- playbooks/ roles/ inventory/
git log -p -- requirements.yml            # an unpinned collection bump
git diff HEAD~1 -- group_vars/
ansible-galaxy collection list            # what versions are actually installed

📚 Finding things

bash
ansible-doc ansible.builtin.copy               # full module docs
ansible-doc -s ansible.builtin.copy            # PASTE-READY task skeleton
ansible-doc -l                                 # every module available
ansible-doc -l | grep -i firewall              # find the right module
ansible-doc -t filter -l                       # every Jinja2 filter
ansible-doc -t filter ansible.builtin.combine  # docs for one filter
ansible-doc -t lookup -l
ansible-doc -t callback -l
ansible-doc -t role mycompany.platform.webserver   # a role's argument_specs
ansible-doc -l mycompany.platform              # modules in one collection
bash
# Test a filter or expression instantly
ansible localhost -m ansible.builtin.debug -a "msg={{ 'test' | upper }}"
ansible localhost -m ansible.builtin.debug -a "msg={{ myvar | type_debug }}"
ansible-doc -s <module> is the single most useful command in this document for day-to-day work. It prints the module's full parameter list already formatted as a task, with defaults as comments. Delete what you do not need and you have a working task in fifteen seconds — without opening a browser.

📦 Collections and roles

bash
ansible-galaxy install -r requirements.yml                   # roles AND collections
ansible-galaxy collection install -r requirements.yml -p ./collections --force
ansible-galaxy collection install community.general:==8.3.0  # exact version
ansible-galaxy collection list
ansible-galaxy role init roles/myrole                        # scaffold a role
ansible-galaxy collection init mycompany.platform            # scaffold a collection
ansible-galaxy collection build --output-path ./dist --force
ansible-galaxy collection install ./dist/mycompany-platform-0.1.0.tar.gz -p ./collections

🔐 Vault

bash
ansible-vault create secrets.yml                         # new encrypted file
ansible-vault edit secrets.yml                           # decrypt, edit, re-encrypt
ansible-vault view secrets.yml                           # read WITHOUT modifying
ansible-vault rekey secrets.yml                          # change the password
ansible-vault encrypt_string 'SuperSecret' --name 'vault_db_password'
ansible-vault encrypt_string --stdin-name 'vault_api_token'   # keeps it out of shell history

ansible-playbook site.yml --vault-password-file ~/.vault-pass
ansible-playbook site.yml --vault-id prod@~/.vault-prod
export ANSIBLE_VAULT_PASSWORD_FILE=~/.vault-pass

The secret audit

bash
# What IS encrypted
grep -rl 'ANSIBLE_VAULT;1.1' . --include='*.yml'

# What SHOULD be - plaintext credentials in version control
git grep -nE '(password|passwd|secret|token|api_key)\s*:\s*[^"{ ]' -- ':!*vault.yml'

# Every secret the project consumes, without decrypting anything
grep -rhoE 'vault_[a-z0-9_]+' --include='vars.yml' . | sort -u
If that second command returns anything, the credential must be rotated — not merely encrypted. The history retains it, and ansible-vault encrypt today does nothing about the commit from three months ago.

🧪 Testing

bash
yamllint .
ansible-lint                                   # lint everything discoverable
ansible-lint --profile production              # the strictest profile
ansible-lint --fix                             # auto-fix the mechanical rules
ansible-lint --list-rules

molecule converge                              # create + run the role, LEAVE IT UP
molecule login -h ubuntu2204                   # shell in and look around
molecule idempotence                           # run again, fail on any change
molecule verify
molecule test                                  # full lifecycle - what CI runs
molecule test -s tls                           # a named scenario

cd ~/collections/ansible_collections/mycompany/platform
ansible-test sanity --docker default
ansible-test sanity --docker --test validate-modules

pre-commit install
pre-commit run --all-files
The local pre-push check — four commands, under a minute:
bash
yamllint .
ansible-lint --profile moderate
ansible-playbook playbooks/site.yml --syntax-check
cd roles/<changed-role> && molecule test

If those pass, CI will almost certainly pass — and you have not burned a pipeline slot discovering a missing quote.


☁️ Dynamic inventory

bash
ansible-inventory -i inventory/prod.aws_ec2.yml --graph
ansible-inventory -i inventory/ --graph --flush-cache        # bypass a stale cache
ansible-inventory -i inventory/ --list --output /tmp/inv.json
ansible -i inventory/ role_web --list-hosts                  # does the pattern match?
ansible -i inventory/ needs_attention --list-hosts           # tagging audit

# When it returns nothing
ansible-inventory -i inventory/x.yml --graph -vvvv 2>&1 | grep -i 'declined\|parse'
ls inventory/                                  # does the filename end in aws_ec2.yml?
aws sts get-caller-identity                    # are credentials working at all?
An empty inventory is a successful run. "No hosts matched" exits zero — so in CI it is a green pipeline that configured nothing. The cause is almost always the filename convention: the aws_ec2 plugin requires its config file to end in aws_ec2.yml or aws_ec2.yaml, and silently declines anything else.

🎛️ AWX

bash
export CONTROLLER_HOST=https://awx.internal
export CONTROLLER_OAUTH_TOKEN="$AWX_TOKEN"          # a SCOPED token, not admin

awx job_templates list
awx job_templates launch "Deploy Web Tier" --monitor            # CI-safe
awx job_templates launch "Deploy Web Tier" \
    --extra_vars '{"app_version":"2.4.1"}' --monitor
awx jobs list --status failed --order_by '-finished' --count 5
awx jobs stdout 4821
awx projects update "Platform Automation" --monitor             # force an SCM pull
awx inventory_sources update "AWS Production" --monitor         # refresh inventory
awx export --organization Engineering > org.json                # config backup
awx roles list --team "Support Desk"                            # who can do what
--monitor is what makes a CI-triggered launch correct. Without it the command returns immediately with exit code 0 — so the pipeline goes green while the deployment is still running, or has already failed. With it, awxkit streams the output and exits non-zero when the job fails.

⚙️ Configuration worth having

plain text
# ansible.cfg
[defaults]
inventory          = ./inventories/staging      # NOT production, deliberately
roles_path         = ./roles:./galaxy_roles
collections_path   = ./collections
remote_user        = deploy
forks              = 25                          # default 5 is far too low
stdout_callback    = yaml
callbacks_enabled  = profile_tasks, timer
interpreter_python = auto_silent
gathering          = smart                       # REQUIRED for fact caching to help
fact_caching       = jsonfile
fact_caching_connection = /tmp/ansible_facts
fact_caching_timeout    = 7200
vault_password_file = ~/.ansible-vault-pass      # the PATH is committed, not the file

[privilege_escalation]
become        = True
become_method = sudo
become_user   = root

[ssh_connection]
pipelining       = True                          # ~40% fewer SSH ops per task
ssh_args         = -o ControlMaster=auto -o ControlPersist=60s
control_path_dir = /tmp/.acp                     # fixes 'socket path too long'

[inventory]
enable_plugins   = auto, yaml, ini, amazon.aws.aws_ec2, constructed
cache            = True
cache_plugin     = jsonfile
cache_timeout    = 900
Two deliberate choices in that file worth understanding rather than copying.

inventory points at staging, so a forgotten -i is harmless rather than an incident. Some teams set no default at all, forcing every command to be explicit.

pipelining = True requires requiretty disabled in sudoers on the managed nodes. Verify across the fleet before enabling it globally:

bash
ansible all -m ansible.builtin.shell -a "grep -r requiretty /etc/sudoers /etc/sudoers.d/ 2>/dev/null || echo OK" -o

🎯 The six patterns worth knowing by heart

Safe fleet rollout
yaml
serial: [1, 5, "25%"]        # canary, then progressive batches
max_fail_percentage: 0       # halt at the first failure
# plus block / rescue / always, where the rescue ENDS with `fail`

Without that fail, a successful rescue clears the failure state, max_fail_percentage never fires, and the rollout continues through a broken release.

The three-step module body
plain text
1. Inspect current state. Matches? -> exit_json(changed=False)
2. if module.check_mode:           -> exit_json(changed=True)
3. Make the change                 -> exit_json(changed=True)

Reverse steps 2 and 3 and your module modifies production during a dry run.

Handler timing
yaml
- ansible.builtin.meta: flush_handlers   # BEFORE any task that verifies the handler's effect

Without it you verify the old state and get a confusing failure.

Idempotency in templates
javascript
{% for host in groups['web'] | sort %}       {# sort = stable = idempotent #}
{% for k, v in settings | dictsort %}        {# same reason #}

Unstable ordering makes the render differ between runs — a false changed and a needless restart, every time.

Type safety on anything from -e
yaml
when: enable_feature | bool                  # -e passes STRINGS; "false" is truthy
Optional module arguments
yaml
owner: "{{ file_owner | default(omit) }}"    # omit = do not pass the argument AT ALL

default('') passes an empty string, which the module then tries to act on and fails.


🚩 The flags that prevent incidents

Flag or settingWhat it prevents
--check --diffApplying a change you have not seen
--limit web01Learning about a bug on 500 hosts instead of one
--list-hostsDiscovering your pattern matched production
validate: nginx -t -c %sWriting a broken config and reloading into an outage
no_log: trueA password in your CI log forever
loop_control: label:The whole credential dict echoed per iteration
max_fail_percentage: 0A bad release reaching every host in the fleet
tags: always on include_varsA tagged run skipping setup and failing mysteriously
apply: on include_roleA tagged run that silently does nothing at all
Pinned requirements.ymlA collection upgrading underneath you with no commit

Every command here is explained in context in the module it came from. This page is for recall during work; the modules are for understanding. When a command here surprises you, that is the signal to reread its module.
Spotted a mistake or want something added? Send me a note.