Introduction

Ansible displays task results in terminal output, log files, and callback plugins. When tasks handle passwords, API keys, or tokens, this data appears in plain text. no_log: true suppresses the output for sensitive tasks, replacing it with "censored" — preventing secret leakage in CI/CD logs, terminal history, and centralized logging systems.

Basic Usage

---
- name: Secure task examples
  hosts: all
  tasks:
    # WITHOUT no_log — password visible in output!
    # TASK [Create user] ****
    # changed: [web01] => {"name": "deploy", "password": "NOT_LOGGING_PASSWORD"...}

    # WITH no_log — output censored
    - name: Create application user
      ansible.builtin.user:
        name: deploy
        password: "{{ vault_deploy_password | password_hash('sha512') }}"
        state: present
      no_log: true
      # TASK [Create user] ****
      # changed: [web01] => {"censored": "the output has been hidden due to the fact that 'no_log: true' was specified for this result"}

Common Use Cases

Database Credentials

    - name: Configure database connection
      ansible.builtin.template:
        src: database.yml.j2
        dest: /opt/app/config/database.yml
        mode: '0600'
      no_log: true
      vars:
        db_password: "{{ vault_db_password }}"

    - name: Create database user
      community.postgresql.postgresql_user:
        name: myapp
        password: "{{ vault_db_password }}"
        db: myapp_production
        priv: ALL
      no_log: true

API Keys and Tokens

    - name: Configure API integration
      ansible.builtin.uri:
        url: "https://api.example.com/v1/configure"
        method: POST
        headers:
          Authorization: "Bearer {{ vault_api_token }}"
        body_format: json
        body:
          setting: value
      no_log: true

    - name: Set environment secrets
      ansible.builtin.lineinfile:
        path: /etc/myapp/environment
        regexp: "^{{ item.key }}="
        line: "{{ item.key }}={{ item.value }}"
        mode: '0600'
      loop:
        - { key: "API_KEY", value: "{{ vault_api_key }}" }
        - { key: "SECRET_KEY", value: "{{ vault_secret_key }}" }
        - { key: "DB_URL", value: "{{ vault_db_url }}" }
      no_log: true

SSL Certificates and Private Keys

    - name: Deploy SSL private key
      ansible.builtin.copy:
        content: "{{ vault_ssl_private_key }}"
        dest: /etc/ssl/private/app.key
        owner: root
        group: ssl-cert
        mode: '0640'
      no_log: true

    - name: Deploy SSL certificate
      ansible.builtin.copy:
        content: "{{ ssl_certificate }}"
        dest: /etc/ssl/certs/app.crt
        mode: '0644'
      # no_log NOT needed — certificates are public

Conditional no_log

    # Only hide in production
    - name: Deploy configuration
      ansible.builtin.template:
        src: app.conf.j2
        dest: /etc/myapp/app.conf
      no_log: "{{ env_name == 'production' }}"

    # Hide based on variable
    - name: Set secret
      ansible.builtin.command:
        cmd: "vault write secret/myapp key={{ secret_value }}"
      no_log: "{{ not (debug_mode | default(false)) }}"

no_log with Loops

    # Hides ALL loop iterations
    - name: Create multiple users
      ansible.builtin.user:
        name: "{{ item.name }}"
        password: "{{ item.password | password_hash('sha512') }}"
      loop: "{{ users }}"
      no_log: true
      # You lose the loop label — can't see which user is being processed

    # Better: show names but hide passwords
    - name: Create multiple users
      ansible.builtin.user:
        name: "{{ item.name }}"
        password: "{{ item.password | password_hash('sha512') }}"
      loop: "{{ users }}"
      loop_control:
        label: "{{ item.name }}"
      no_log: true

no_log with register

    # The registered variable is also censored in debug output
    - name: Get secret from vault
      ansible.builtin.command:
        cmd: vault kv get -field=password secret/myapp
      register: secret_result
      no_log: true

    # This debug will show the censored message, not the actual value
    - name: Try to show secret (won't work)
      ansible.builtin.debug:
        var: secret_result
      # Output: "censored"

    # But you CAN use the variable in subsequent tasks
    - name: Use the secret
      ansible.builtin.template:
        src: config.j2
        dest: /etc/myapp/config.yml
      vars:
        app_password: "{{ secret_result.stdout }}"
      no_log: true  # Also hide this task's output

Global no_log Setting

# ansible.cfg — NOT recommended for production
[defaults]
# Show no_log values (DANGEROUS — only for debugging)
display_args_to_stdout = false
no_log = false  # Override all no_log directives
# Temporarily show censored output for debugging
ANSIBLE_NO_LOG=false ansible-playbook site.yml
# ⚠️ NEVER do this in CI/CD or shared environments

What no_log Hides

HiddenNot Hidden
Task arguments (module params)Task name
Return values (stdout, result)Task status (ok/changed/failed)
Loop item valuesHost name
Registered variable contentTask duration
Debug output of no_log varsError messages (partially)

Troubleshooting

IssueSolution
Can't debug a failing no_log taskTemporarily set no_log: false, fix, re-enable
CI/CD logs show secretsAdd no_log: true to ALL tasks handling secrets
Ansible Lint warning no-log-passwordLint correctly detected missing no_log — add it
no_log on debug taskPointless — debug exists to show output; rethink approach
Error message leaks secretError output may still contain args; use failed_when to catch

ansible-lint Integration

# ansible-lint automatically warns about missing no_log
# Rule: no-log-password
ansible-lint playbook.yml

# Example warning:
# playbook.yml:15: no-log-password: Tasks that set passwords should use no_log

Best Practices

  1. Always no_log on password tasks — user, mysql_user, postgresql_user
  2. Always no_log on API auth tasks — uri with Authorization headers
  3. Always no_log on key/cert tasks — private key deployments
  4. Use loop_control.label — preserve useful output while hiding secrets
  5. Run ansible-lint — catches missing no_log automatically
  6. Don't no_log everything — it makes debugging impossible
  7. Pair with Vault — no_log hides output; Vault encrypts at rest

Conclusion

no_log: true is a one-line addition that prevents secrets from leaking into terminal output, CI/CD logs, and monitoring systems. Apply it to every task that handles passwords, tokens, keys, or connection strings. Combined with Ansible Vault for encryption at rest, no_log completes the secret protection story — encrypted in files, hidden in output.