When using Ansible to automate tasks, the command
module is your bread and butter for executing system commands. But did you know that there’s a safer, cleaner, and more predictable way to pass arguments? Meet argv
—an alternative to writing commands as strings.
In this post, I’ll explore the pros and cons of using argv
, and I’ll walk through several real-world examples tailored to web servers and mail servers.
Why Use argv
Instead of a Command String?
✅ Pros
- Avoids Shell Parsing Issues: Each argument is passed exactly as intended, with no surprises from quoting or spaces.
- More Secure: No shell = no risk of shell injection.
- Clearer Syntax: Every argument is explicitly defined, improving readability.
- Predictable: Behavior is consistent across different platforms and setups.
❌ Cons
- No Shell Features: You can’t use pipes (
|
), redirection (>
), or environment variables like$HOME
. - More Verbose: Every argument must be a separate list item. It’s explicit, but more to type.
- Not for Shell Built-ins: Commands like
cd
,export
, orecho
with redirection won’t work.
Real-World Examples
Let’s apply this to actual use cases.
🔧 Restarting Nginx with argv
- name: Restart Nginx using argv
hosts: amedee.be
become: yes
tasks:
- name: Restart Nginx
ansible.builtin.command:
argv:
- systemctl
- restart
- nginx
📬 Check Mail Queue on a Mail-in-a-Box Server
- name: Check Postfix mail queue using argv
hosts: box.vangasse.eu
become: yes
tasks:
- name: Get mail queue status
ansible.builtin.command:
argv:
- mailq
register: mail_queue
- name: Show queue
ansible.builtin.debug:
msg: "{{ mail_queue.stdout_lines }}"
🗃️ Back Up WordPress Database
- name: Backup WordPress database using argv
hosts: amedee.be
become: yes
vars:
db_user: wordpress_user
db_password: wordpress_password
db_name: wordpress_db
tasks:
- name: Dump database
ansible.builtin.command:
argv:
- mysqldump
- -u
- "{{ db_user }}"
- -p{{ db_password }}
- "{{ db_name }}"
- --result-file=/root/wordpress_backup.sql
⚠️ Avoid exposing credentials directly—use Ansible Vault instead.
Using argv
with Interpolation
Ansible lets you use Jinja2-style variables ({{ }}
) inside argv
items.
🔄 Restart a Dynamic Service
- name: Restart a service using argv and variable
hosts: localhost
become: yes
vars:
service_name: nginx
tasks:
- name: Restart
ansible.builtin.command:
argv:
- systemctl
- restart
- "{{ service_name }}"
🕒 Timestamped Backups
- name: Timestamped DB backup
hosts: localhost
become: yes
vars:
db_user: wordpress_user
db_password: wordpress_password
db_name: wordpress_db
tasks:
- name: Dump with timestamp
ansible.builtin.command:
argv:
- mysqldump
- -u
- "{{ db_user }}"
- -p{{ db_password }}
- "{{ db_name }}"
- --result-file=/root/wordpress_backup_{{ ansible_date_time.iso8601 }}.sql
🧩 Dynamic Argument Lists
Avoid join(' ')
, which collapses the list into a single string.
❌ Wrong:
argv:
- ls
- "{{ args_list | join(' ') }}" # BAD: becomes one long string
✅ Correct:
argv: ["ls"] + args_list
Or if the length is known:
argv:
- ls
- "{{ args_list[0] }}"
- "{{ args_list[1] }}"
📣 Interpolation Inside Strings
- name: Greet with hostname
hosts: localhost
tasks:
- name: Print message
ansible.builtin.command:
argv:
- echo
- "Hello, {{ ansible_facts['hostname'] }}!"
When to Use argv
✅ Commands with complex quoting or multiple arguments
✅ Tasks requiring safety and predictability
✅ Scripts or binaries that take arguments, but not full shell expressions
When to Avoid argv
❌ When you need pipes, redirection, or shell expansion
❌ When you’re calling shell built-ins
Final Thoughts
Using argv
in Ansible may feel a bit verbose, but it offers precision and security that traditional string commands lack. When you need reliable, cross-platform automation that avoids the quirks of shell parsing, argv
is the better choice.
Prefer safety? Choose argv
.
Need shell magic? Use the shell
module.
Have a favorite argv
trick or horror story? Drop it in the comments below.