The no_log: true directive in Ansible prevents sensitive data — passwords, API keys, tokens, and certificates — from appearing in playbook output and log files. Without it, Ansible logs every task's input and output in plain text, which can expose secrets in CI/CD logs, terminal output, and callback plugin data.
How no_log Works
When you add no_log: true to a task, Ansible replaces the task output with "censored" in all logging destinations:
- name: Create database user with password
community.mysql.mysql_user:
name: app_user
password: "{{ db_password }}"
priv: "mydb.*:ALL"
state: present
no_log: true
Without no_log, the output shows:
TASK [Create database user with password] ****
changed: [db-server] => {"changed": true, "password": "s3cr3t_p@ssw0rd", ...}
With no_log: true, the output shows:
TASK [Create database user with password] ****
changed: [db-server] => {"censored": "the output has been hidden due to the fact that 'no_log: true' was specified for this result"}
When to Use no_log
Always Use no_log For
- Password and credential tasks: Creating users, database accounts, service credentials
- API key operations: Registering tokens, setting up integrations
- Certificate and private key handling: Deploying TLS certificates
- Cloud provider credentials: AWS access keys, Azure service principals
- Vault-encrypted variable usage: Any task that decrypts and uses vault secrets
Common Examples
User Management with Passwords
- name: Create application user
ansible.builtin.user:
name: deploy
password: "{{ deploy_password | password_hash('sha512') }}"
state: present
no_log: true
API Token Registration
- name: Register service with API token
ansible.builtin.uri:
url: "https://api.example.com/register"
method: POST
headers:
Authorization: "Bearer {{ api_token }}"
body_format: json
body:
service: myapp
no_log: true
register: api_result
Deploying TLS Certificates
- name: Deploy SSL private key
ansible.builtin.copy:
content: "{{ ssl_private_key }}"
dest: /etc/ssl/private/server.key
owner: root
group: root
mode: '0600'
no_log: true
Cloud Provider Credentials
- name: Configure AWS credentials
ansible.builtin.template:
src: aws_credentials.j2
dest: /home/deploy/.aws/credentials
owner: deploy
mode: '0600'
no_log: true
Applying no_log at Different Levels
Task Level (Most Common)
- name: Task with sensitive data
ansible.builtin.debug:
msg: "{{ secret_variable }}"
no_log: true
Block Level
Apply to multiple tasks at once:
- block:
- name: Create database
community.mysql.mysql_db:
name: myapp
state: present
- name: Create database user
community.mysql.mysql_user:
name: app_user
password: "{{ db_password }}"
priv: "mydb.*:ALL"
state: present
- name: Grant additional privileges
community.mysql.mysql_query:
query: "GRANT REPLICATION SLAVE ON *.* TO 'app_user'@'%'"
no_log: true
Play Level
Hide all tasks in an entire play:
- hosts: secrets_servers
no_log: true
tasks:
- name: Deploy all secrets
ansible.builtin.copy:
src: "{{ item }}"
dest: "/etc/secrets/{{ item | basename }}"
loop: "{{ secret_files }}"
Role Level (via Role Defaults)
Set no_log as a role variable:
# roles/deploy_certs/defaults/main.yml
deploy_certs_no_log: true
# roles/deploy_certs/tasks/main.yml
- name: Deploy certificate
ansible.builtin.copy:
content: "{{ cert_content }}"
dest: "{{ cert_path }}"
no_log: "{{ deploy_certs_no_log }}"
Conditional no_log
Use a variable to toggle no_log for debugging:
# In group_vars or command line
# ansible-playbook site.yml -e "show_sensitive=true"
- name: Create service account
ansible.builtin.user:
name: service_account
password: "{{ svc_password | password_hash('sha512') }}"
no_log: "{{ not (show_sensitive | default(false) | bool) }}"
This lets you temporarily disable no_log during development while keeping it enabled in production.
Debugging no_log Tasks
When a task with no_log: true fails, debugging is difficult because the error message is censored. Strategies:
1. Temporarily Disable no_log
# For debugging only — NEVER commit this change
- name: Create user
ansible.builtin.user:
name: app_user
password: "{{ password }}"
# no_log: true # Temporarily commented out
2. Use the Conditional Approach
- name: Create user
ansible.builtin.user:
name: app_user
password: "{{ password }}"
no_log: "{{ ansible_verbosity < 3 }}"
With -vvv verbosity, no_log is disabled. With normal verbosity, it's enabled.
3. Check Variables Separately
- name: Verify password variable is defined
ansible.builtin.assert:
that:
- db_password is defined
- db_password | length > 0
fail_msg: "db_password is not set"
- name: Create database user
community.mysql.mysql_user:
name: app_user
password: "{{ db_password }}"
no_log: true
4. Use the Ansible Debugger
- name: Create user with debugger
ansible.builtin.user:
name: app_user
password: "{{ password }}"
no_log: true
debugger: on_failed
When the task fails, the debugger opens and you can inspect variables interactively.
Combining no_log with Ansible Vault
For maximum security, combine no_log with Ansible Vault encrypted variables:
# Encrypt the variables file
# ansible-vault encrypt group_vars/production/secrets.yml
# group_vars/production/secrets.yml (encrypted)
db_password: "s3cr3t_p@ssw0rd"
api_token: "tok_abc123def456"
# playbook.yml
- name: Deploy application
hosts: production
tasks:
- name: Configure database connection
ansible.builtin.template:
src: db_config.j2
dest: /etc/myapp/database.yml
no_log: true
Why both? Vault encrypts secrets at rest (in files). no_log protects them at runtime (in output). Together, they provide defense in depth.
Common Mistakes
1. Forgetting no_log on Registered Variables
# BAD — password exposed in later debug task
- name: Get API token
ansible.builtin.uri:
url: https://api.example.com/auth
method: POST
body: '{"password": "{{ api_password }}"}'
register: auth_result
- name: Show result
ansible.builtin.debug:
var: auth_result # Exposes the password in the request body!
# GOOD
- name: Get API token
ansible.builtin.uri:
url: https://api.example.com/auth
method: POST
body: '{"password": "{{ api_password }}"}'
register: auth_result
no_log: true
- name: Show only the token
ansible.builtin.debug:
msg: "Token starts with: {{ auth_result.json.token[:4] }}..."
2. Using no_log on Non-Sensitive Tasks
Don't overuse no_log — it makes debugging harder. Only apply it to tasks that actually handle sensitive data.
3. Ignoring Callback Plugins
Some callback plugins (like ARA) store task results. no_log affects these too — the censored output is what gets stored. If you use ARA or similar tools, verify that sensitive data is indeed hidden in the recorded results.
ansible-lint Rule
The ansible-lint tool includes rule no-log-password that warns when tasks using password-related parameters lack no_log:
$ ansible-lint playbook.yml
WARNING Listing 1 violation(s) that are fatal
no-log-password: password parameter should have no_log
playbook.yml:5 Task/Handler: Create database user
Links
Related Articles
- Ansible Vault: Encrypt and Decrypt Playbook Files
- Ansible Error Handling: blocks rescue always
- Ansible Debug Module Guide
- Ansible Debugger Step-by-Step
- Ansible Best Practices for Production Environments
- Ansible Lightspeed Complete Guide
- Understanding Ansible Roles
Conclusion
The no_log directive is a critical security control for any Ansible automation that handles sensitive data. Apply it consistently to tasks involving passwords, API keys, tokens, and certificates. Combine it with Ansible Vault for defense in depth, use conditional no_log for debugging flexibility, and rely on ansible-lint to catch missing no_log directives in your CI/CD pipeline.